diff --git a/src/consensus/state.rs b/src/consensus/state.rs index ddca5dd03..ff4f1b334 100644 --- a/src/consensus/state.rs +++ b/src/consensus/state.rs @@ -156,11 +156,16 @@ impl ConsensusState { } /// Generate current slot checkpoint - fn generate_slot_checkpoint(&mut self, sigma1: pallas::Base, sigma2: pallas::Base) { + fn generate_slot_checkpoint( + &mut self, + fork_hashes: Vec, + sigma1: pallas::Base, + sigma2: pallas::Base, + ) { let slot = self.time_keeper.current_slot(); - let eta = self.get_eta(); - info!(target: "consensus::state", "generate_slot_checkpoint: slot: {:?}, eta: {:?}", slot, eta); - let checkpoint = SlotCheckpoint { slot, eta, sigma1, sigma2 }; + let previous_eta = self.get_previous_eta(); + let checkpoint = SlotCheckpoint { slot, previous_eta, fork_hashes, sigma1, sigma2 }; + info!(target: "consensus::state", "generate_slot_checkpoint: {:?}", checkpoint); self.slot_checkpoints.push(checkpoint); } @@ -176,10 +181,11 @@ impl ConsensusState { /// Returns flag to signify if epoch has changed. pub async fn epoch_changed( &mut self, + fork_hashes: Vec, sigma1: pallas::Base, sigma2: pallas::Base, ) -> Result { - self.generate_slot_checkpoint(sigma1, sigma2); + self.generate_slot_checkpoint(fork_hashes, sigma1, sigma2); let epoch = self.time_keeper.current_epoch(); if epoch <= self.epoch { return Ok(false) @@ -423,7 +429,7 @@ impl ConsensusState { let first_winning = coin.is_leader( sigma1, sigma2, - self.get_eta(), + self.get_previous_eta(), pallas::Base::from(self.time_keeper.current_slot()), ); @@ -534,7 +540,7 @@ impl ConsensusState { /// Utility function to extract leader selection lottery randomness(eta), /// defined as the hash of the last block, converted to pallas base. - pub fn get_eta(&self) -> pallas::Base { + pub fn get_previous_eta(&self) -> pallas::Base { let (_, hash) = self.blockchain.last().unwrap(); let mut bytes: [u8; 32] = *hash.as_bytes(); // Read first 254 bits @@ -584,6 +590,22 @@ impl ConsensusState { } } + /// Retrieve current forks last proposal hashes. + /// If node holds no fork, retrieve last canonical hash. + pub fn fork_hashes(&self) -> Vec { + let mut hashes = vec![]; + for fork in &self.forks { + hashes.push(fork.sequence.last().unwrap().proposal.hash); + } + + if hashes.is_empty() { + let (_, hash) = self.blockchain.last().unwrap(); + hashes.push(hash); + } + + hashes + } + /// Auxiliary structure to reset consensus state for a resync pub fn reset(&mut self) { self.participating = None; @@ -668,10 +690,11 @@ impl net::Message for ConsensusSlotCheckpointsResponse { pub struct SlotCheckpoint { /// Slot UID pub slot: u64, - /// Slot eta - // TODO: this should be renamed to previous_eta, - // corresponding to previous block eta. - pub eta: pallas::Base, + /// Previous slot eta + pub previous_eta: pallas::Base, + /// Previous slot forks last proposal/block hashes, + /// as observed by the validator + pub fork_hashes: Vec, /// Slot sigma1 pub sigma1: pallas::Base, /// Slot sigma2 @@ -679,17 +702,24 @@ pub struct SlotCheckpoint { } impl SlotCheckpoint { - pub fn new(slot: u64, eta: pallas::Base, sigma1: pallas::Base, sigma2: pallas::Base) -> Self { - Self { slot, eta, sigma1, sigma2 } + pub fn new( + slot: u64, + previous_eta: pallas::Base, + fork_hashes: Vec, + sigma1: pallas::Base, + sigma2: pallas::Base, + ) -> Self { + Self { slot, previous_eta, fork_hashes, sigma1, sigma2 } } /// Generate the genesis slot checkpoint. pub fn genesis_slot_checkpoint() -> Self { - let eta = pallas::Base::zero(); + let previous_eta = pallas::Base::zero(); + let fork_hashes = vec![]; let sigma1 = pallas::Base::zero(); let sigma2 = pallas::Base::zero(); - Self::new(0, eta, sigma1, sigma2) + Self::new(0, previous_eta, fork_hashes, sigma1, sigma2) } } diff --git a/src/consensus/task/proposal.rs b/src/consensus/task/proposal.rs index 3e1562cba..1bac4f8b2 100644 --- a/src/consensus/task/proposal.rs +++ b/src/consensus/task/proposal.rs @@ -186,10 +186,14 @@ async fn propose_period(consensus_p2p: P2pPtr, state: ValidatorStatePtr) -> bool // Keep a record of slot to verify if next slot got skipped during processing let processing_slot = state.read().await.consensus.time_keeper.current_slot(); + // Retrieve current forks last hash + let fork_hashes = state.read().await.consensus.fork_hashes(); + // Retrieve slot sigmas let (sigma1, sigma2) = state.write().await.consensus.sigmas(); // Node checks if epoch has changed and generate slot checkpoint - let epoch_changed = state.write().await.consensus.epoch_changed(sigma1, sigma2).await; + let epoch_changed = + state.write().await.consensus.epoch_changed(fork_hashes, sigma1, sigma2).await; match epoch_changed { Ok(changed) => { if changed { diff --git a/src/consensus/validator.rs b/src/consensus/validator.rs index b62568e07..7c3c6958f 100644 --- a/src/consensus/validator.rs +++ b/src/consensus/validator.rs @@ -362,7 +362,7 @@ impl ValidatorState { sigma1: pallas::Base, sigma2: pallas::Base, ) -> Result> { - let eta = self.consensus.get_eta(); + let eta = self.consensus.get_previous_eta(); // Check if node can produce proposals if !self.consensus.proposing { return Ok(None) @@ -576,7 +576,7 @@ impl ValidatorState { // Validate proposal public value against coin creation slot checkpoint let (mu_y, mu_rho) = LeadCoin::election_seeds_u64( - self.consensus.get_eta(), + self.consensus.get_previous_eta(), self.consensus.time_keeper.current_slot(), ); // y diff --git a/src/contract/consensus/src/client/proposal_v1.rs b/src/contract/consensus/src/client/proposal_v1.rs index 0b1a3cbb4..c1f4141e3 100644 --- a/src/contract/consensus/src/client/proposal_v1.rs +++ b/src/contract/consensus/src/client/proposal_v1.rs @@ -278,10 +278,8 @@ pub fn create_proposal_proof( let slot_pallas = pallas::Base::from(slot_checkpoint.slot); let seed = poseidon_hash([SEED_PREFIX, input.note.serial]); - // NOTE: slot checkpoint eta to be renamed to previous_eta, - // corresponding to previous block eta. let mut vrf_input = Vec::with_capacity(32 + 32); - vrf_input.extend_from_slice(&slot_checkpoint.eta.to_repr()); + vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr()); vrf_input.extend_from_slice(&slot_pallas.to_repr()); let vrf_proof = VrfProof::prove(input.secret, &vrf_input, &mut OsRng); let mut eta = [0u8; 64]; diff --git a/src/contract/consensus/src/entrypoint/proposal_v1.rs b/src/contract/consensus/src/entrypoint/proposal_v1.rs index e61d375be..90ee67eed 100644 --- a/src/contract/consensus/src/entrypoint/proposal_v1.rs +++ b/src/contract/consensus/src/entrypoint/proposal_v1.rs @@ -97,10 +97,8 @@ pub(crate) fn consensus_proposal_get_metadata_v1( // Verify eta VRF proof let slot_pallas = pallas::Base::from(slot_checkpoint.slot); - // NOTE: slot checkpoint eta to be renamed to previous_eta, - // corresponding to previous block eta. let mut vrf_input = Vec::with_capacity(32 + 32); - vrf_input.extend_from_slice(&slot_checkpoint.eta.to_repr()); + vrf_input.extend_from_slice(&slot_checkpoint.previous_eta.to_repr()); vrf_input.extend_from_slice(&slot_pallas.to_repr()); let vrf_proof = ¶ms.vrf_proof; if !vrf_proof.verify(params.input.signature_public, &vrf_input) { diff --git a/src/contract/consensus/src/model.rs b/src/contract/consensus/src/model.rs index 794e6d94a..bcfc67a31 100644 --- a/src/contract/consensus/src/model.rs +++ b/src/contract/consensus/src/model.rs @@ -113,8 +113,11 @@ pub const HEADSTART: pallas::Base = pallas::Base::from_raw([ pub struct SlotCheckpoint { /// Slot UID pub slot: u64, - /// Slot eta - pub eta: pallas::Base, + /// Previous slot eta + pub previous_eta: pallas::Base, + /// Previous slot forks last proposal/block hashes, + /// as observed by the validator + pub fork_hashes: Vec, /// Slot sigma1 pub sigma1: pallas::Base, /// Slot sigma2 diff --git a/src/contract/consensus/tests/harness.rs b/src/contract/consensus/tests/harness.rs index 576d69c65..9a1cc7221 100644 --- a/src/contract/consensus/tests/harness.rs +++ b/src/contract/consensus/tests/harness.rs @@ -850,10 +850,13 @@ impl ConsensusTestHarness { pub async fn generate_slot_checkpoint(&self, slot: u64) -> Result { // We grab the genesis slot to generate slot checkpoint // using same consensus parameters + let faucet = self.holders.get(&Holder::Faucet).unwrap(); + let fork_hashes = vec![faucet.state.read().await.consensus.genesis_block]; let genesis_slot = self.get_slot_checkpoint_by_slot(0).await?; let slot_checkpoint = SlotCheckpoint { slot, - eta: genesis_slot.eta, + previous_eta: genesis_slot.previous_eta, + fork_hashes, sigma1: genesis_slot.sigma1, sigma2: genesis_slot.sigma2, };