diff --git a/crates/core/app/src/app/mod.rs b/crates/core/app/src/app/mod.rs index a2cc146e68..4ea641438d 100644 --- a/crates/core/app/src/app/mod.rs +++ b/crates/core/app/src/app/mod.rs @@ -34,6 +34,7 @@ use penumbra_stake::component::{ use penumbra_transaction::Transaction; use prost::Message as _; use tendermint::abci::{self, Event}; +use tendermint_proto::v0_34::abci::Event as ProtoEvent; use tendermint::v0_37::abci::{request, response}; use tendermint::validator::Update; @@ -151,6 +152,22 @@ impl App { } }; + // Apply the state from `init_chain` and return the events generated during genesis. These + // cannot be emitted at this time due to a limitation of ABCI, but they will be emitted + // during the first BeginBlock. + let events = state_tx.apply().1; + + // Commit the genesis events to the state. + let mut state_tx = self + .state + .try_begin_transaction() + .expect("state Arc should not be referenced elsewhere"); + for (i, event) in events.into_iter().enumerate() { + let mut event_bytes = Vec::new(); + tendermint_proto::Protobuf::::encode(event, &mut event_bytes) + .expect("events are always valid and can be encoded"); + state_tx.nonverifiable_put_raw(state_key::deferred_event(i), event_bytes); + } state_tx.apply(); } @@ -219,6 +236,23 @@ impl App { pub async fn begin_block(&mut self, begin_block: &request::BeginBlock) -> Vec { let mut state_tx = StateDelta::new(self.state.clone()); + // Before anything else, emit the deferred events (only present if right after genesis), and + // delete each one to prevent it from being emitted again. + for i in 0.. { + if let Some(event_bytes) = state_tx + .nonverifiable_get_raw(&state_key::deferred_event(i)) + .await + .expect("can attempt to get deferred event") + { + let event = tendermint_proto::Protobuf::::decode(&event_bytes[..]) + .expect("deferred event is always valid"); + state_tx.record(event); + state_tx.nonverifiable_delete(state_key::deferred_event(i)); + } else { + break; + } + } + // If a app parameter change is scheduled for this block, apply it here, // before any other component has executed. This ensures that app // parameter changes are consistently applied precisely at the boundary diff --git a/crates/core/app/src/app/state_key.rs b/crates/core/app/src/app/state_key.rs index cddac82b45..7c3801bcfc 100644 --- a/crates/core/app/src/app/state_key.rs +++ b/crates/core/app/src/app/state_key.rs @@ -10,6 +10,12 @@ pub mod data { } } +pub fn deferred_event(index: usize) -> Vec { + format!("application/deferred_events/{index}") + .as_bytes() + .to_vec() +} + pub mod cometbft_data { use crate::COMETBFT_SUBSTORE_PREFIX;