Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node: open session using prev_state_root #3194

Merged
merged 6 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading