Partial integration of the Era Supervisor with the Reactor#42
Conversation
| let storage = Storage::new(cfg.storage)?; | ||
| let (api_server, api_server_effects) = ApiServer::new(cfg.http_server, effect_builder); | ||
| let (consensus, consensus_effects) = Consensus::new(effect_builder); | ||
| let consensus = EraSupervisor::new(); |
There was a problem hiding this comment.
Nitpick: this is a bit leaking – reactor shouldn't have to know or care that there's something like eras or EraSupervisor.
There was a problem hiding this comment.
Yes, we "merged" Consensus and EraSupervisor, but maybe it should be called Consensus.
| /// The piece of information that will become the content of a future block (isn't finalized or | ||
| /// executed yet) | ||
| #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
| pub struct ProtoBlock { |
There was a problem hiding this comment.
It's missing more block information like seq number, panorama, signature etc. I guess that will come later? If so, can we create a ticket for it already?
There was a problem hiding this comment.
I don't think it will contain the panorama or any signatures; that would be redundant, and an implementation detail of the consensus algorithm.
There was a problem hiding this comment.
I must be missing something – is this data transferred over the wire to other nodes?
There was a problem hiding this comment.
BTW, Isn't this ProtoBlock specific to Highway only? I thought that messages that will be transmitted over the wire will be generic in that.
There was a problem hiding this comment.
The ProtoBlock is precisely meant to be the Highway/Casper-independent part of the block: the "consensus value".
So it is transferred over the wire, as part of a Highway block.
There was a problem hiding this comment.
The
ProtoBlockis precisely meant to be the Highway/Casper-independent part of the block: the "consensus value".
Ah, that isn't obvious. There's no comment that explains the intent and the name is also misleading. It even says proto but we won't be using protobuf most probably.
There was a problem hiding this comment.
It's proto as in prototype, not as in protocol ;)
| #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
| pub struct ExecutedBlock { | ||
| /// The executed proto-block | ||
| pub proto_block: ProtoBlock, |
There was a problem hiding this comment.
Why not just refer to the proto_block by its hash?
There was a problem hiding this comment.
We could, but it might complicate things a bit: Then we'd need something like a proto block store as well…
Also: Once it's executed we don't really need to proto block alone anymore, do we?
There was a problem hiding this comment.
We will need that anyway – in case of crashes, restarts, catching up?
There was a problem hiding this comment.
But if it has already been added to the Highway protocol state, it would be part of it. (If we don't just store it as a hash.) So if we persist the protocol state, we automatically also persist the consensus values, i.e. the proto blocks.
If we don't want it in the protocol state, and use hashes instead, then we'd need a separate storage for them, yes.
There was a problem hiding this comment.
ProtocolState is ephemeral and we need the finalized linear chain to be persistent.
| } | ||
|
|
||
| /// Checks whether the deploys included in the proto-block exist on the network | ||
| pub(crate) async fn validate_proto_block<I>( |
There was a problem hiding this comment.
Shouldn't this be done in the synchronizer of the consensus protocol?
There was a problem hiding this comment.
We were thinking it makes more sense for another component (deploy fetcher?) to do this.
But it does happen after what the synchronizer does (downloading dependencies) and before adding the vertex to the DAG.
| CreateNewBlock, | ||
| ScheduleTimer(u64, TimerId), | ||
| /// Request deploys for a new block, whose timestamp will be the given `u64`. | ||
| CreateNewBlock(u64), |
There was a problem hiding this comment.
It's not entirely clear to me how this message is going to be handled. Is it returned from the ConsensusProtocol to reactor? Or just EraSupervisor that will have access to a DeployBuffer (or some other deploy storage)? Shouldn't it also (besides the block timestamp) also carry an information about consensus value proposed between last finalized block and the current tip? I understand that it's not part of this task but we should leave a comment that is complete and clear.
There was a problem hiding this comment.
Yes, the intention here is that another component (TBD) will select deploys.
I agree, a TODO makes sense. Although it's hard to "forget": We'll simply need that information once we try to handle deploy dependencies.
There was a problem hiding this comment.
I don't understand why we would need yet another component for that. Only an active validator can ever produce new blocks so it would make sense to hide this logic there.
There was a problem hiding this comment.
But it means that the consensus component — in addition to all the stuff it already deals with — needs to care about block gas limits, size limits, deploy dependencies, etc.
And it seems like it would be a relatively small interface between consensus and deploy buffer, if we make that separate.
| } | ||
|
|
||
| #[derive(Clone, Debug)] | ||
| struct EraInstance<Id> { |
There was a problem hiding this comment.
I think it's not used anywhere? At least I can't find it.
| } | ||
|
|
||
| /// Passes the timestamp of a future block for which deploys are to be proposed | ||
| pub(crate) async fn request_proto_block(self, instant: u64) -> ProtoBlock { |
There was a problem hiding this comment.
Is request a good verb here? Is it going to actually make any requests or just accept all the necessary information in the argument list?
There was a problem hiding this comment.
Well, it's a "request" from the consensus component to some other component (deploy buffer?) to select a bunch of deploys. Not sure if it's a good word.
There was a problem hiding this comment.
So it's related to another comment https://github.com/CasperLabs/casperlabs-node/pull/42/files#r448237398 . In any case, it should probably also carry an information about all the deploys that are already known to the current consensus instance.
There was a problem hiding this comment.
Yes, let's add a TODO for that, too: It'll probably be a nontrivial issue in itself to add that.
| .ignore(), | ||
| ConsensusProtocolResult::ScheduleTimer(_timestamp, _timer_id) => { | ||
| // TODO: we need to get the current system time here somehow, in order to schedule | ||
| // a timer for the correct moment - and we don't want to use std::Instant |
There was a problem hiding this comment.
Isn't timestamp already a "real" moment in time in the future?
There was a problem hiding this comment.
Why do we don't want to use std::Instant?
There was a problem hiding this comment.
Instants are purely local (i.e. subjective), monotonically increasing timestamps: You can't communicate and agree on an Instant among different nodes. Highway assumes synchronized clocks and the timer needs to be scheduled at a particular "absolute" point in time (i.e. not relative to any particular local event).
See the discussion on Slack: https://casperlabs-team.slack.com/archives/C015VHEF532/p1593530821205400
| )); | ||
| } | ||
| Ok(SynchronizerEffect::InvalidVertex(v, sender, err)) => { | ||
| warn!("Invalid vertex from {:?}: {:?}, {:?}", v, sender, err); |
There was a problem hiding this comment.
There's an InvalidIncomingMessage variant in the ConsensusProtocolResult enum.
| /// Request deploys for a new block, whose timestamp will be the given `u64`. | ||
| CreateNewBlock(u64), | ||
| FinalizedBlock(C), | ||
| ValidateConsensusValue(NodeId, C), |
There was a problem hiding this comment.
What does it mean to "validate a consensus value"? Can you leave a comment with a TODO what needs to be done? It's not clear to me whether "validating" means checking signatures (this should be done at the moment of receiving the deploy)? Downloading it from the peers (then it shouldn't be propagated to the reactor)? Or something different?
There was a problem hiding this comment.
Maybe:
/// A consensus message we received from the specified node contains a consensus value.
// TODO: Add a `ConsensusProtocol::consensus_value_validated` method to call back after validation.Since C is generic here I wouldn't refer to ProtoBlocks, deploys and downloading then from the sender. That part should be explained in the era supervisor, where the consensus value is now ProtoBlock and not a generic argument anymore.
Anyway, concretely it means: "Make sure you have and validated all deploys mentioned in this ProtoBlock."
There was a problem hiding this comment.
The only validations I can imagine performed on a deploy should be either done at the moment of receiving it or are consensus-instance specific. I can't think of anything that would require contact with an external component.
There was a problem hiding this comment.
The idea here is that consensus values might have some intrinsic validity conditions (not related to signatures or the consensus protocol itself), so when you receive a proposal for a value from another node, you might want to check whether the value can actually be legally proposed. In our case, this intrinsic condition is the existence of all the deploys mentioned by hash within the value (proto-block), but the general idea could also be applied to other use cases.
There was a problem hiding this comment.
OK, I wasn't aware that you changed the plan about where the syncing of consensus value will happen.
There was a problem hiding this comment.
But the component that will usually be receiving and validating deploys (also gossiped pending ones) — do you think that should also be the consensus component?
| CreateNewBlock, | ||
| ScheduleTimer(u64, TimerId), | ||
| /// Request deploys for a new block, whose timestamp will be the given `u64`. | ||
| CreateNewBlock(u64), |
There was a problem hiding this comment.
Yes, the intention here is that another component (TBD) will select deploys.
I agree, a TODO makes sense. Although it's hard to "forget": We'll simply need that information once we try to handle deploy dependencies.
| /// Request deploys for a new block, whose timestamp will be the given `u64`. | ||
| CreateNewBlock(u64), | ||
| FinalizedBlock(C), | ||
| ValidateConsensusValue(NodeId, C), |
There was a problem hiding this comment.
Maybe:
/// A consensus message we received from the specified node contains a consensus value.
// TODO: Add a `ConsensusProtocol::consensus_value_validated` method to call back after validation.Since C is generic here I wouldn't refer to ProtoBlocks, deploys and downloading then from the sender. That part should be explained in the era supervisor, where the consensus value is now ProtoBlock and not a generic argument anymore.
Anyway, concretely it means: "Make sure you have and validated all deploys mentioned in this ProtoBlock."
| .ignore(), | ||
| ConsensusProtocolResult::ScheduleTimer(_timestamp, _timer_id) => { | ||
| // TODO: we need to get the current system time here somehow, in order to schedule | ||
| // a timer for the correct moment - and we don't want to use std::Instant |
There was a problem hiding this comment.
Instants are purely local (i.e. subjective), monotonically increasing timestamps: You can't communicate and agree on an Instant among different nodes. Highway assumes synchronized clocks and the timer needs to be scheduled at a particular "absolute" point in time (i.e. not relative to any particular local event).
See the discussion on Slack: https://casperlabs-team.slack.com/archives/C015VHEF532/p1593530821205400
| } | ||
|
|
||
| /// Passes the timestamp of a future block for which deploys are to be proposed | ||
| pub(crate) async fn request_proto_block(self, instant: u64) -> ProtoBlock { |
There was a problem hiding this comment.
Well, it's a "request" from the consensus component to some other component (deploy buffer?) to select a bunch of deploys. Not sure if it's a good word.
| } | ||
|
|
||
| /// Checks whether the deploys included in the proto-block exist on the network | ||
| pub(crate) async fn validate_proto_block<I>( |
There was a problem hiding this comment.
We were thinking it makes more sense for another component (deploy fetcher?) to do this.
But it does happen after what the synchronizer does (downloading dependencies) and before adding the vertex to the DAG.
| let storage = Storage::new(cfg.storage)?; | ||
| let (api_server, api_server_effects) = ApiServer::new(cfg.http_server, effect_builder); | ||
| let (consensus, consensus_effects) = Consensus::new(effect_builder); | ||
| let consensus = EraSupervisor::new(); |
There was a problem hiding this comment.
Yes, we "merged" Consensus and EraSupervisor, but maybe it should be called Consensus.
| /// The piece of information that will become the content of a future block (isn't finalized or | ||
| /// executed yet) | ||
| #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
| pub struct ProtoBlock { |
There was a problem hiding this comment.
I don't think it will contain the panorama or any signatures; that would be redundant, and an implementation detail of the consensus algorithm.
| #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
| pub struct ExecutedBlock { | ||
| /// The executed proto-block | ||
| pub proto_block: ProtoBlock, |
There was a problem hiding this comment.
We could, but it might complicate things a bit: Then we'd need something like a proto block store as well…
Also: Once it's executed we don't really need to proto block alone anymore, do we?
goral09
left a comment
There was a problem hiding this comment.
No blockers, few requests for a TODO.
This PR wires up some events and effects of the Era Supervisor with the Reactor. There are also some changes to the implementation of the synchronizer for the Highway protocol.
https://casperlabs.atlassian.net/browse/HWY-74
https://casperlabs.atlassian.net/browse/HWY-87