Skip to content

Commit

Permalink
Merge pull request #3194 from dusk-network/verify_state_root
Browse files Browse the repository at this point in the history
node: open session using prev_state_root
  • Loading branch information
herr-seppia authored Dec 19, 2024
2 parents 71a27dd + 169ae69 commit 567c280
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 53 deletions.
6 changes: 6 additions & 0 deletions consensus/src/commons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct RoundUpdate {

seed: Seed,
hash: [u8; 32],
state_root: [u8; 32],
att: Attestation,
att_voters: Vec<Voter>,
timestamp: u64,
Expand All @@ -59,6 +60,7 @@ impl RoundUpdate {
timestamp: tip_header.timestamp,
base_timeouts,
att_voters,
state_root: tip_header.state_hash,
}
}

Expand All @@ -81,6 +83,10 @@ impl RoundUpdate {
pub fn att_voters(&self) -> &Vec<Voter> {
&self.att_voters
}

pub fn state_root(&self) -> [u8; 32] {
self.state_root
}
}

#[async_trait::async_trait]
Expand Down
32 changes: 31 additions & 1 deletion consensus/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl From<BlsSigError> for ConsensusError {
#[derive(Debug, Error)]
pub enum OperationError {
#[error("failed to call VST {0}")]
InvalidVST(anyhow::Error),
InvalidVST(VstError),
#[error("failed to call EST {0}")]
InvalidEST(anyhow::Error),
#[error("failed to verify header {0}")]
Expand Down Expand Up @@ -116,6 +116,36 @@ pub enum HeaderError {
Storage(&'static str, anyhow::Error),
}

#[derive(Debug, Error)]
pub enum VstError {
#[error(
"mismatch, event_bloom: {}, candidate_event_bloom: {}",
hex::encode(.0.as_ref()),
hex::encode(.1.as_ref())
)]
MismatchEventBloom(Box<[u8; 256]>, Box<[u8; 256]>),
#[error(
"mismatch, state_hash: {}, candidate_state_hash: {}",
hex::encode(.0),
hex::encode(.1)
)]
MismatchStateHash([u8; 32], [u8; 32]),
#[error("Chain tip different from the expected one")]
TipChanged,
#[error("Invalid slash from block: {0}")]
InvalidSlash(io::Error),
#[error("Invalid generator: {0:?}")]
InvalidGenerator(dusk_bytes::Error),
#[error("Generic error in vst: {0}")]
Generic(String),
}

impl VstError {
pub fn must_vote(&self) -> bool {
!matches!(self, Self::TipChanged)
}
}

impl HeaderError {
pub fn must_vote(&self) -> bool {
match self {
Expand Down
4 changes: 3 additions & 1 deletion consensus/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct CallParams {
pub to_slash: Vec<Slash>,
pub voters_pubkey: Vec<Voter>,
pub max_txs_bytes: usize,
pub prev_state_root: StateRoot,
}

#[derive(Default)]
Expand Down Expand Up @@ -77,9 +78,10 @@ pub trait Operations: Send + Sync {

async fn verify_state_transition(
&self,
prev_commit: StateRoot,
blk: &Block,
voters: &[Voter],
) -> Result<VerificationOutput, OperationError>;
) -> Result<VerificationOutput, VstError>;

async fn execute_state_transition(
&self,
Expand Down
1 change: 1 addition & 0 deletions consensus/src/proposal/block_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ impl<T: Operations> Generator<T> {
to_slash,
voters_pubkey: voters.to_owned(),
max_txs_bytes,
prev_state_root: ru.state_root(),
};

let result =
Expand Down
65 changes: 36 additions & 29 deletions consensus/src/validation/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use std::sync::Arc;

use anyhow::anyhow;
use node_data::bls::PublicKeyBytes;
use node_data::ledger::{to_str, Block};
use node_data::message::payload::{Validation, Vote};
Expand All @@ -19,6 +18,7 @@ use tracing::{debug, error, info, Instrument};

use crate::commons::{Database, RoundUpdate};
use crate::config::is_emergency_iter;
use crate::errors::VstError;
use crate::execution_ctx::ExecutionCtx;
use crate::msg_handler::StepOutcome;
use crate::operations::{Operations, Voter};
Expand Down Expand Up @@ -103,10 +103,21 @@ impl<T: Operations + 'static, D: Database> ValidationStep<T, D> {
error!(event = "invalid faults", ?err);
Vote::Invalid(header.hash)
} else {
match Self::call_vst(candidate, &voters, &executor).await {
match Self::call_vst(
ru.state_root(),
candidate,
&voters,
&executor,
)
.await
{
Ok(_) => Vote::Valid(header.hash),
Err(err) => {
error!(event = "failed_vst_call", ?err);
let voting = err.must_vote();
error!(event = "invalid_vst", ?err, voting);
if !voting {
return;
}
Vote::Invalid(header.hash)
}
}
Expand Down Expand Up @@ -154,36 +165,32 @@ impl<T: Operations + 'static, D: Database> ValidationStep<T, D> {
}

async fn call_vst(
prev_commit: [u8; 32],
candidate: &Block,
voters: &[Voter],
executor: &Arc<T>,
) -> anyhow::Result<()> {
match executor.verify_state_transition(candidate, voters).await {
Ok(output) => {
// Ensure the `event_bloom` and `state_root` returned
// from the VST call are the
// ones we expect to have with the
// current candidate block.
if output.event_bloom != candidate.header().event_bloom {
return Err(anyhow!(
"mismatch, event_bloom: {}, candidate_event_bloom: {}",
hex::encode(output.event_bloom),
hex::encode(candidate.header().event_bloom)
));
}
) -> Result<(), VstError> {
let output = executor
.verify_state_transition(prev_commit, candidate, voters)
.await?;

if output.state_root != candidate.header().state_hash {
return Err(anyhow!(
"mismatch, state_hash: {}, candidate_state_hash: {}",
hex::encode(output.state_root),
hex::encode(candidate.header().state_hash)
));
}
}
Err(err) => {
return Err(anyhow!("vm_err: {:?}", err));
}
};
// Ensure the `event_bloom` and `state_root` returned
// from the VST call are the
// ones we expect to have with the
// current candidate block.
if output.event_bloom != candidate.header().event_bloom {
return Err(VstError::MismatchEventBloom(
Box::new(output.event_bloom),
Box::new(candidate.header().event_bloom),
));
}

if output.state_root != candidate.header().state_hash {
return Err(VstError::MismatchStateHash(
output.state_root,
candidate.header().state_hash,
));
}

Ok(())
}
Expand Down
10 changes: 7 additions & 3 deletions node/src/chain/acceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ impl<DB: database::DB, VM: vm::VMExecution, N: Network> Acceptor<N, DB, VM> {
let mut task = self.task.write().await;

let mut tip = self.tip.write().await;
let prev_header = tip.inner().header().clone();
let mut provisioners_list = self.provisioners_list.write().await;
let block_time =
blk.header().timestamp - tip.inner().header().timestamp;
Expand All @@ -636,7 +637,7 @@ impl<DB: database::DB, VM: vm::VMExecution, N: Network> Acceptor<N, DB, VM> {
// Verify Block Header
let (pni, prev_block_voters, tip_block_voters) = verify_block_header(
self.db.clone(),
&tip.inner().header().clone(),
&prev_header,
&provisioners_list,
blk.header(),
)
Expand All @@ -658,8 +659,11 @@ impl<DB: database::DB, VM: vm::VMExecution, N: Network> Acceptor<N, DB, VM> {
let vm = self.vm.write().await;

let (stakes, finality) = self.db.read().await.update(|db| {
let (txs, verification_output, stake_events) =
vm.accept(blk, &prev_block_voters[..])?;
let (txs, verification_output, stake_events) = vm.accept(
prev_header.state_hash,
blk,
&prev_block_voters[..],
)?;
for spent_tx in txs.iter() {
events.push(TransactionEvent::Executed(spent_tx).into());
}
Expand Down
10 changes: 6 additions & 4 deletions node/src/chain/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use std::time::Duration;
use async_trait::async_trait;
use dusk_consensus::commons::{RoundUpdate, TimeoutSet};
use dusk_consensus::consensus::Consensus;
use dusk_consensus::errors::{ConsensusError, HeaderError, OperationError};
use dusk_consensus::errors::{
ConsensusError, HeaderError, OperationError, VstError,
};
use dusk_consensus::operations::{
CallParams, Operations, Output, VerificationOutput, Voter,
};
Expand Down Expand Up @@ -315,15 +317,15 @@ impl<DB: database::DB, VM: vm::VMExecution> Operations for Executor<DB, VM> {

async fn verify_state_transition(
&self,
prev_root: [u8; 32],
blk: &Block,
voters: &[Voter],
) -> Result<VerificationOutput, OperationError> {
) -> Result<VerificationOutput, VstError> {
info!("verifying state");

let vm = self.vm.read().await;

vm.verify_state_transition(blk, voters)
.map_err(OperationError::InvalidVST)
vm.verify_state_transition(prev_root, blk, voters)
}

async fn execute_state_transition(
Expand Down
5 changes: 4 additions & 1 deletion node/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use dusk_consensus::errors::VstError;
use dusk_consensus::operations::{CallParams, VerificationOutput, Voter};
use dusk_consensus::user::provisioners::Provisioners;
use dusk_consensus::user::stake::Stake;
Expand All @@ -29,12 +30,14 @@ pub trait VMExecution: Send + Sync + 'static {

fn verify_state_transition(
&self,
prev_root: [u8; 32],
blk: &Block,
voters: &[Voter],
) -> anyhow::Result<VerificationOutput>;
) -> Result<VerificationOutput, VstError>;

fn accept(
&self,
prev_root: [u8; 32],
blk: &Block,
voters: &[Voter],
) -> anyhow::Result<(
Expand Down
2 changes: 2 additions & 0 deletions rusk/benches/block_ingestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ fn bench_accept(
let generator = PublicKey::new(*DUSK_CONSENSUS_KEY).into_inner();

let txs = Arc::new(txs);
let prev_root = rusk.state_root();

for n_txs in N_TXS {
let rusk = rusk.clone();
Expand All @@ -131,6 +132,7 @@ fn bench_accept(
let txs = txs[..*n_txs].to_vec();

rusk.accept_transactions(
prev_root,
BLOCK_HEIGHT,
BLOCK_GAS_LIMIT,
BLOCK_HASH,
Expand Down
5 changes: 5 additions & 0 deletions rusk/src/lib/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum Error {
InvalidCreditsCount(u64, usize),
/// Memo too large
MemoTooLarge(usize),
/// Chain tip different from the expected one
TipChanged,
}

impl std::error::Error for Error {}
Expand Down Expand Up @@ -182,6 +184,9 @@ impl fmt::Display for Error {
Error::MemoTooLarge(size) => {
write!(f, "The memo size {size} is too large")
}
Error::TipChanged => {
write!(f, "Chain tip different from the expected one")
}
}
}
}
20 changes: 14 additions & 6 deletions rusk/src/lib/node/rusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,12 @@ impl Rusk {
let block_gas_limit = self.block_gas_limit;
let generator = params.generator_pubkey.inner();
let to_slash = params.to_slash.clone();
let prev_state_root = params.prev_state_root;

let voters = &params.voters_pubkey[..];

let mut session = self.new_block_session(block_height, None)?;
let mut session =
self.new_block_session(block_height, prev_state_root)?;

let mut block_gas_left = block_gas_limit;

Expand Down Expand Up @@ -179,7 +181,8 @@ impl Rusk {
// transaction, since it is technically valid.
if gas_spent > block_gas_left {
info!("Skipping {tx_id_hex} due gas_spent {gas_spent} greater than left: {block_gas_left}");
session = self.new_block_session(block_height, None)?;
session = self
.new_block_session(block_height, prev_state_root)?;

for spent_tx in &spent_txs {
// We know these transactions were correctly
Expand Down Expand Up @@ -259,6 +262,7 @@ impl Rusk {
#[allow(clippy::too_many_arguments)]
pub fn verify_transactions(
&self,
prev_commit: [u8; 32],
block_height: u64,
block_hash: Hash,
block_gas_limit: u64,
Expand All @@ -267,7 +271,7 @@ impl Rusk {
slashing: Vec<Slash>,
voters: &[Voter],
) -> Result<(Vec<SpentTransaction>, VerificationOutput)> {
let session = self.new_block_session(block_height, None)?;
let session = self.new_block_session(block_height, prev_commit)?;

accept(
session,
Expand All @@ -292,6 +296,7 @@ impl Rusk {
#[allow(clippy::too_many_arguments)]
pub fn accept_transactions(
&self,
prev_commit: [u8; 32],
block_height: u64,
block_gas_limit: u64,
block_hash: Hash,
Expand All @@ -305,7 +310,7 @@ impl Rusk {
VerificationOutput,
Vec<ContractEvent>,
)> {
let session = self.new_block_session(block_height, None)?;
let session = self.new_block_session(block_height, prev_commit)?;

let (spent_txs, verification_output, session, events) = accept(
session,
Expand Down Expand Up @@ -472,9 +477,12 @@ impl Rusk {
pub(crate) fn new_block_session(
&self,
block_height: u64,
commit: Option<[u8; 32]>,
commit: [u8; 32],
) -> Result<Session> {
let mut session = self._session(block_height, commit)?;
let mut session = self._session(block_height, None)?;
if session.root() != commit {
return Err(Error::TipChanged);
}
let _: CallReceipt<()> = session
.call(STAKE_CONTRACT, "before_state_transition", &(), u64::MAX)
.expect("before_state_transition to success");
Expand Down
Loading

0 comments on commit 567c280

Please sign in to comment.