Explorar o código

consensus/BlockProposal: added header as part of struct

This was done so we can execute faster validations, as previously we were using proposal.block.header.headerhash() each time, which serialized the header and produced a blake3 hash.
aggstam %!s(int64=3) %!d(string=hai) anos
pai
achega
7bcc75b65e
Modificáronse 4 ficheiros con 41 adicións e 20 borrados
  1. 9 5
      src/consensus/block.rs
  2. 1 1
      src/consensus/proto/protocol_proposal.rs
  3. 28 14
      src/consensus/state.rs
  4. 3 0
      src/error.rs

+ 9 - 5
src/consensus/block.rs

@@ -216,6 +216,8 @@ impl net::Message for BlockResponse {
 /// This struct represents a block proposal, used for consensus.
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct BlockProposal {
+    /// Block header hash
+    pub header: blake3::Hash,
     /// Block data
     pub block: BlockInfo,
 }
@@ -224,13 +226,16 @@ impl BlockProposal {
     #[allow(clippy::too_many_arguments)]
     pub fn new(header: Header, txs: Vec<Transaction>, metadata: Metadata) -> Self {
         let block = BlockInfo::new(header, txs, metadata);
-        Self { block }
+        let header = block.header.headerhash();
+        Self { header, block }
     }
 }
 
 impl PartialEq for BlockProposal {
     fn eq(&self, other: &Self) -> bool {
-        self.block.header == other.block.header && self.block.txs == other.block.txs
+        self.header == other.header &&
+            self.block.header == other.block.header &&
+            self.block.txs == other.block.txs
     }
 }
 
@@ -239,7 +244,7 @@ impl fmt::Display for BlockProposal {
         formatter.write_fmt(format_args!(
             "BlockProposal {{ leader addr: {}, hash: {}, epoch: {}, slot: {}, txs: {} }}",
             self.block.metadata.address,
-            self.block.header.headerhash(),
+            self.header,
             self.block.header.epoch,
             self.block.header.slot,
             self.block.txs.len()
@@ -281,8 +286,7 @@ impl ProposalChain {
             return false
         }
 
-        let prev_hash = previous.block.header.headerhash();
-        if proposal.block.header.previous != prev_hash ||
+        if proposal.block.header.previous != previous.header ||
             proposal.block.header.slot <= previous.block.header.slot
         {
             debug!("check_proposal(): Provided proposal is invalid.");

+ 1 - 1
src/consensus/proto/protocol_proposal.rs

@@ -81,7 +81,7 @@ impl ProtocolProposal {
             let proposal_copy = (*proposal).clone();
 
             // Verify we have the proposal already
-            if self.state.read().await.proposal_exists(&proposal_copy.block.header.headerhash()) {
+            if self.state.read().await.proposal_exists(&proposal_copy.header) {
                 debug!("ProtocolProposal::handle_receive_proposal(): Proposal already received.");
                 continue
             }

+ 28 - 14
src/consensus/state.rs

@@ -435,7 +435,7 @@ impl ValidatorState {
         }
 
         let hash = match longest {
-            Some(chain) => chain.proposals.last().unwrap().block.header.headerhash(),
+            Some(chain) => chain.proposals.last().unwrap().header,
             None => self.blockchain.last()?.1,
         };
 
@@ -460,12 +460,7 @@ impl ValidatorState {
             None => return Ok(None),
         }
 
-        // Check if proposal extends any existing fork chains
-        let index = self.find_extended_chain_index(proposal)?;
-        if index == -2 {
-            return Err(Error::ExtendedChainIndexNotFoundError)
-        }
-
+        // Check if leader is a known consensus participant
         let leader = self.consensus.participants.get(&proposal.block.metadata.address);
         if leader.is_none() {
             warn!(
@@ -476,6 +471,17 @@ impl ValidatorState {
         }
         let leader = leader.unwrap();
 
+        // Check if proposal header matches actual one
+        let proposal_header = proposal.block.header.headerhash();
+        if proposal.header != proposal_header {
+            warn!(
+                "receive_proposal(): Received proposal contains missmatched headers: {} - {}",
+                proposal.header, proposal_header
+            );
+            return Err(Error::ProposalHeadersMissmatchError)
+        }
+
+        // Verify proposal winning coin public inputs match known ones
         let public_inputs = &leader.coins[self.relative_slot(current) as usize]
             [proposal.block.metadata.winning_index];
         if public_inputs != &proposal.block.metadata.public_inputs {
@@ -483,6 +489,7 @@ impl ValidatorState {
             return Err(Error::InvalidPublicInputsError)
         }
 
+        // Verify proposal leader proof
         match proposal.block.metadata.proof.verify(&self.verifying_key, public_inputs) {
             Ok(_) => info!("receive_proposal(): Proof veryfied succsessfully!"),
             Err(e) => {
@@ -491,10 +498,9 @@ impl ValidatorState {
             }
         }
 
-        if !leader.public_key.verify(
-            proposal.block.header.headerhash().as_bytes(),
-            &proposal.block.metadata.signature,
-        ) {
+        // Verify proposal signature is valid based on leader known valid key
+        if !leader.public_key.verify(proposal.header.as_bytes(), &proposal.block.metadata.signature)
+        {
             warn!(
                 "receive_proposal(): Proposer ({}) signature could not be verified",
                 proposal.block.metadata.address
@@ -502,6 +508,14 @@ impl ValidatorState {
             return Err(Error::InvalidSignatureError)
         }
 
+        // Check if proposal extends any existing fork chains
+        let index = self.find_extended_chain_index(proposal)?;
+        if index == -2 {
+            return Err(Error::ExtendedChainIndexNotFoundError)
+        }
+
+        // Validate state transition against canonical state
+        // TODO: This should be validated against fork state
         debug!("receive_proposal(): Starting state transition validation");
         let canon_state_clone = self.state_machine.lock().await.clone();
         let mem_state = MemoryState::new(canon_state_clone);
@@ -518,6 +532,7 @@ impl ValidatorState {
 
         // TODO: [PLACEHOLDER] Add rewards validation
 
+        // Check if proposal fork has can be finalized, to broadcast those blocks
         let mut to_broadcast = vec![];
         match index {
             -1 => {
@@ -546,7 +561,7 @@ impl ValidatorState {
         let mut fork = None;
         for (index, chain) in self.consensus.proposals.iter().enumerate() {
             let last = chain.proposals.last().unwrap();
-            let hash = last.block.header.headerhash();
+            let hash = last.header;
             if proposal.block.header.previous == hash &&
                 proposal.block.header.slot > last.block.header.slot
             {
@@ -584,8 +599,7 @@ impl ValidatorState {
     pub fn proposal_exists(&self, input_proposal: &blake3::Hash) -> bool {
         for chain in self.consensus.proposals.iter() {
             for proposal in chain.proposals.iter() {
-                let proposal_hash = proposal.block.header.headerhash();
-                if input_proposal == &proposal_hash {
+                if input_proposal == &proposal.header {
                     return true
                 }
             }

+ 3 - 0
src/error.rs

@@ -229,6 +229,9 @@ pub enum Error {
     #[error("Check if proposal extends any existing fork chains failed")]
     ExtendedChainIndexNotFoundError,
 
+    #[error("Proposal contains missmatched headers")]
+    ProposalHeadersMissmatchError,
+
     // ===============
     // Database errors
     // ===============