battleware_execution/
state_transition.rs

1use crate::{Adb, Layer, State};
2use battleware_types::{
3    execution::{Output, Seed, Transaction, Value},
4    Identity, NAMESPACE,
5};
6use commonware_cryptography::{ed25519::PublicKey, sha256::Digest, Sha256};
7#[cfg(feature = "parallel")]
8use commonware_runtime::ThreadPool;
9use commonware_runtime::{Clock, Metrics, Spawner, Storage};
10use commonware_storage::{adb::keyless, mmr::hasher::Standard, translator::Translator};
11use std::collections::BTreeMap;
12
13/// Result of executing a block's state transition
14pub struct StateTransitionResult {
15    pub state_root: Digest,
16    pub state_start_op: u64,
17    pub state_end_op: u64,
18    pub events_root: Digest,
19    pub events_start_op: u64,
20    pub events_end_op: u64,
21    /// Map of public keys to their next expected nonce after processing
22    pub processed_nonces: BTreeMap<PublicKey, u64>,
23}
24
25/// Execute state transition for a block
26///
27/// This function processes all transactions in a block, updating both state and events
28/// databases. It handles transaction nonce validation, execution, and persistence.
29/// Only processes the block if it's the next expected height.
30///
31/// Returns the resulting state and events roots along with their operation counts,
32/// plus a map of processed public keys to their next expected nonces.
33pub async fn execute_state_transition<S: Spawner + Storage + Clock + Metrics, T: Translator>(
34    state: &mut Adb<S, T>,
35    events: &mut keyless::Keyless<S, Output, Sha256>,
36    identity: Identity,
37    height: u64,
38    seed: Seed,
39    transactions: Vec<Transaction>,
40    #[cfg(feature = "parallel")] pool: ThreadPool,
41) -> StateTransitionResult {
42    // Check if this is the next expected height for state
43    let (state_height, mut state_start_op) = state
44        .get_metadata()
45        .await
46        .unwrap()
47        .and_then(|(_, v)| match v {
48            Some(Value::Commit { height, start }) => Some((height, start)),
49            _ => None,
50        })
51        .unwrap_or((0, 0));
52    assert!(
53        height == state_height || height == state_height + 1,
54        "state transition must be for next block or tip"
55    );
56
57    // Get events metadata
58    let (events_height, mut events_start_op) = events
59        .get_metadata()
60        .await
61        .unwrap()
62        .and_then(|(_, v)| match v {
63            Some(Output::Commit { height, start }) => Some((height, start)),
64            _ => None,
65        })
66        .unwrap_or((0, 0));
67
68    // Only process if this is the next block
69    let mut processed_nonces = BTreeMap::new();
70    if height == state_height + 1 {
71        state_start_op = state.op_count();
72        let mut layer = Layer::new(state, identity, NAMESPACE, seed);
73        let (outputs, nonces) = layer
74            .execute(
75                #[cfg(feature = "parallel")]
76                pool,
77                transactions,
78            )
79            .await;
80        processed_nonces.extend(nonces);
81
82        // Apply events if this is the next block
83        if height == events_height + 1 {
84            events_start_op = events.op_count();
85            for output in outputs.into_iter() {
86                events.append(output).await.unwrap();
87            }
88            events
89                .commit(Some(Output::Commit {
90                    height,
91                    start: events_start_op,
92                }))
93                .await
94                .unwrap();
95        }
96
97        // Apply state once we've committed events (can't regenerate after state updated)
98        state.apply(layer.commit()).await;
99        state
100            .commit(Some(Value::Commit {
101                height,
102                start: state_start_op,
103            }))
104            .await
105            .unwrap();
106    }
107
108    // Compute roots
109    let mut mmr_hasher = Standard::<Sha256>::new();
110    let state_root = state.root(&mut mmr_hasher);
111    let state_end_op = state.op_count();
112    let events_root = events.root(&mut mmr_hasher);
113    let events_end_op = events.op_count();
114
115    StateTransitionResult {
116        state_root,
117        state_start_op,
118        state_end_op,
119        events_root,
120        events_start_op,
121        events_end_op,
122        processed_nonces,
123    }
124}