Repository: aptos-labs/aptos-core
Pull request #16638: [vm] Event validation with closures
Author: login: georgemitenkov
Created: 2025-05-15T14:25:08Z | Merged: 2025-05-23T16:03:47Z

## Description

With closures, event validation needs to be adjusted to account for `0x1::event::emit` captured in a closure. This PR improves event validation and event natives to account for that:

1. For scripts, any event emission is disallowed (i.e., for any function handle, it should not be `0x1::event::emit`). As scripts have no struct definitions, there is nothing to emit.
2. Native `emit` implementation ensure the caller is known. When the caller is `None` (i.e., the call comes from script), an error is also returned.
3. For modules, in addition to direct static calls to `0x1::event::emit`, we also check any closure that is packed from `0x1::event::emit`. This is sufficient to enforce that all event structs come from the same module at publish time.

## How Has This Been Tested?

- New tests for scripts
- New tests for modules

## Key Areas to Review
<!--
- Identify any critical parts of the code that require special attention or understanding. Explain why these parts are crucial to the functionality or architecture of the project.
- Point out any areas where complex logic has been implemented. Provide a brief explanation of the logic and your approach to make it easier for reviewers to follow.
- Highlight any areas where you are particularly concerned or unsure about the code's impact on the change. This can include potential performance or security issues, or compatibility with existing features.
-->

## Type of Change

- [x] New feature
- [x] Bug fix
- [x] Tests

## Which Components or Systems Does This Change Impact?

- [x] Move/Aptos Virtual Machine
- [x] Aptos Framework

## Checklist
- [x] I have read and followed the [CONTRIBUTING](https://github.com/aptos-labs/aptos-core/blob/main/CONTRIBUTING.md) doc
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I identified and added all stakeholders and component owners affected by this change as reviewers
- [x] I tested both happy and unhappy path of the functionality
- [x] I have made corresponding changes to the documentation

<!-- Thank you for your contribution! -->



Diff:
diff --git a/aptos-move/aptos-vm/src/verifier/event_validation.rs b/aptos-move/aptos-vm/src/verifier/event_validation.rs
index 71733b5151e..675fef03b81 100644
--- a/aptos-move/aptos-vm/src/verifier/event_validation.rs
+++ b/aptos-move/aptos-vm/src/verifier/event_validation.rs
@@ -6,10 +6,11 @@ use aptos_types::{
     vm::module_metadata::{get_metadata_from_compiled_code, RuntimeModuleMetadataV1},
 };
 use move_binary_format::{
-    access::{ModuleAccess, ScriptAccess},
+    access::ModuleAccess,
+    binary_views::BinaryIndexedView,
     errors::{Location, PartialVMError, VMError, VMResult},
     file_format::{
-        Bytecode, CompiledScript,
+        Bytecode, CompiledScript, FunctionHandle,
         SignatureToken::{Struct, StructInstantiation},
     },
     CompiledModule,
@@ -71,7 +72,23 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks regular calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
+/// }
+/// ```
 pub(crate) fn validate_emit_calls(
     event_structs: &HashSet<String>,
     module: &CompiledModule,
@@ -79,44 +96,135 @@ pub(crate) fn validate_emit_calls(
     for fun in module.function_defs() {
         if let Some(code_unit) = &fun.code {
             for bc in &code_unit.code {
-                if let Bytecode::CallGeneric(index) = bc {
-                    let func_instantiation = &module.function_instantiation_at(*index);
-                    let func_handle = module.function_handle_at(func_instantiation.handle);
-                    let module_handle = module.module_handle_at(func_handle.module);
-                    let module_addr = module.address_identifier_at(module_handle.address);
-                    let module_name = module.identifier_at(module_handle.name);
-                    let func_name = module.identifier_at(func_handle.name);
-                    if module_addr != &AccountAddress::ONE
-                        || module_name.as_str() != EVENT_MODULE_NAME
-                        || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
-                    {
-                        continue;
-                    }
-                    let param = module
-                        .signature_at(func_instantiation.type_parameters)
-                        .0
-                        .first()
-                        .ok_or_else(|| {
-                            metadata_validation_error(
-                                "Missing parameter for 0x1::event::emit function",
-                            )
-                        })?;
-                    match param {
-                        StructInstantiation(index, _) | Struct(index) => {
-                            let struct_handle = &module.struct_handle_at(*index);
-                            let struct_name = module.identifier_at(struct_handle.name);
-                            if struct_handle.module != module.self_handle_idx() {
-                                metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
-                            } else if !event_structs.contains(struct_name.as_str()) {
-                                metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
-                            } else {
-                                Ok(())
-                            }
-                        },
-                        _ => metadata_validation_err(
-                            "Passed in a non-struct parameter into 0x1::event::emit.",
-                        ),
-                    }?;
+                use Bytecode::*;
+                match bc {
+                    CallGeneric(index) | PackClosureGeneric(index, ..) => {
+                        let func_instantiation = &module.function_instantiation_at(*index);
+                        let func_handle = module.function_handle_at(func_instantiation.handle);
+
+                        if !is_event_emit_call(BinaryIndexedView::Module(module), func_handle) {
+                            continue;
+                        }
+
+                        let param = module
+                            .signature_at(func_instantiation.type_parameters)
+                            .0
+                            .first()
+                            .ok_or_else(|| {
+                                metadata_validation_error(
+                                    "Missing parameter for 0x1::event::emit function",
+                                )
+                            })?;
+                        match param {
+                            StructInstantiation(index, _) | Struct(index) => {
+                                let struct_handle = &module.struct_handle_at(*index);
+                                let struct_name = module.identifier_at(struct_handle.name);
+                                if struct_handle.module != module.self_handle_idx() {
+                                    metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
+                                } else if !event_structs.contains(struct_name.as_str()) {
+                                    metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
+                                } else {
+                                    Ok(())
+                                }
+                            },
+                            _ => metadata_validation_err(
+                                "Passed in a non-struct parameter into 0x1::event::emit.",
+                            ),
+                        }?;
+                    },
+                    // Note: If a closure is packed, it cannot be 0x1::event::emit, but the lifted
+                    // lambda body may contain the emit function, and so will match the case above.
+                    // For all other instructions, no validation. We specifically do a full match
+                    // here to ensure that when a new bytecode gets added, compiler complains and
+                    // the validation pass is revisited.
+                    PackClosure(_, _)
+                    | VecPack(_, _)
+                    | VecLen(_)
+                    | VecImmBorrow(_)
+                    | VecMutBorrow(_)
+                    | VecPushBack(_)
+                    | VecPopBack(_)
+                    | VecUnpack(_, _)
+                    | VecSwap(_)
+                    | CallClosure(_)
+                    | Pop
+                    | Ret
+                    | BrTrue(_)
+                    | BrFalse(_)
+                    | Branch(_)
+                    | LdU8(_)
+                    | LdU16(_)
+                    | LdU32(_)
+                    | LdU64(_)
+                    | LdU128(_)
+                    | LdU256(_)
+                    | CastU8
+                    | CastU16
+                    | CastU32
+                    | CastU64
+                    | CastU128
+                    | CastU256
+                    | LdConst(_)
+                    | LdTrue
+                    | LdFalse
+                    | CopyLoc(_)
+                    | MoveLoc(_)
+                    | StLoc(_)
+                    | MutBorrowLoc(_)
+                    | ImmBorrowLoc(_)
+                    | MutBorrowField(_)
+                    | ImmBorrowField(_)
+                    | MutBorrowFieldGeneric(_)
+                    | ImmBorrowFieldGeneric(_)
+                    | Call(_)
+                    | Pack(_)
+                    | PackGeneric(_)
+                    | Unpack(_)
+                    | UnpackGeneric(_)
+                    | Exists(_)
+                    | ExistsGeneric(_)
+                    | MutBorrowGlobal(_)
+                    | ImmBorrowGlobal(_)
+                    | MutBorrowGlobalGeneric(_)
+                    | ImmBorrowGlobalGeneric(_)
+                    | MoveFrom(_)
+                    | MoveFromGeneric(_)
+                    | MoveTo(_)
+                    | MoveToGeneric(_)
+                    | FreezeRef
+                    | ReadRef
+                    | WriteRef
+                    | Add
+                    | Sub
+                    | Mul
+                    | Mod
+                    | Div
+                    | BitOr
+                    | BitAnd
+                    | Xor
+                    | Shl
+                    | Shr
+                    | Or
+                    | And
+                    | Not
+                    | Eq
+                    | Neq
+                    | Lt
+                    | Gt
+                    | Le
+                    | Ge
+                    | Abort
+                    | Nop
+                    | ImmBorrowVariantField(_)
+                    | ImmBorrowVariantFieldGeneric(_)
+                    | MutBorrowVariantField(_)
+                    | MutBorrowVariantFieldGeneric(_)
+                    | PackVariant(_)
+                    | PackVariantGeneric(_)
+                    | UnpackVariant(_)
+                    | UnpackVariantGeneric(_)
+                    | TestVariant(_)
+                    | TestVariantGeneric(_) => (),
                 }
             }
         }
@@ -139,23 +247,44 @@ pub(crate) fn extract_event_metadata(
     Ok(event_structs)
 }
 
+/// Returns an error if the script uses 0x1::event::emit function (whether as a direct call, or as
+/// a closure). Note that this is not overly restrictive: even a callback to emit an event cannot
+/// be passed, i.e. the following script should fail:
+///
+/// ```move
+/// script {
+///   fun main() {
+///     let f = |e| {
+///       // ... do something here ...
+///       0x1::event::emit(e);
+///     }
+///
+///     // This call creates an event and calls `f` on it.
+///     0x123::some_module::some_function(f);
+///   }
+/// }
+/// ```
+///
+/// This is ok to fail here, as event emission should be done by the module where event is defined.
 pub(crate) fn verify_no_event_emission_in_compiled_script(script: &CompiledScript) -> VMResult<()> {
-    for bc in &script.code().code {
-        if let Bytecode::CallGeneric(index) = bc {
-            let func_instantiation = &script.function_instantiation_at(*index);
-            let func_handle = script.function_handle_at(func_instantiation.handle);
-            let module_handle = script.module_handle_at(func_handle.module);
-            let module_addr = script.address_identifier_at(module_handle.address);
-            let module_name = script.identifier_at(module_handle.name);
-            let func_name = script.identifier_at(func_handle.name);
-            if module_addr == &AccountAddress::ONE
-                && module_name.as_str() == EVENT_MODULE_NAME
-                && func_name.as_str() == EVENT_EMIT_FUNCTION_NAME
-            {
-                return Err(PartialVMError::new(StatusCode::INVALID_OPERATION_IN_SCRIPT)
-                    .finish(Location::Script));
-            }
+    for func_handle in &script.function_handles {
+        if is_event_emit_call(BinaryIndexedView::Script(script), func_handle) {
+            debug_assert!(func_handle.type_parameters.len() == 1);
+            return Err(PartialVMError::new(StatusCode::INVALID_OPERATION_IN_SCRIPT)
+                .finish(Location::Script));
         }
     }
     Ok(())
 }
+
+/// Returns true if the handle corresponds to `0x1::event::emit` function call.
+fn is_event_emit_call(view: BinaryIndexedView, func_handle: &FunctionHandle) -> bool {
+    let module_handle = view.module_handle_at(func_handle.module);
+    let module_addr = view.address_identifier_at(module_handle.address);
+    let module_name = view.identifier_at(module_handle.name);
+    let func_name = view.identifier_at(func_handle.name);
+
+    module_addr == &AccountAddress::ONE
+        && module_name.as_str() == EVENT_MODULE_NAME
+        && func_name.as_str() == EVENT_EMIT_FUNCTION_NAME
+}
diff --git a/aptos-move/e2e-move-tests/src/tests/module_event.rs b/aptos-move/e2e-move-tests/src/tests/module_event.rs
index bc3310bedc2..075be01a92d 100644
--- a/aptos-move/e2e-move-tests/src/tests/module_event.rs
+++ b/aptos-move/e2e-move-tests/src/tests/module_event.rs
@@ -2,8 +2,12 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::{assert_success, assert_vm_status, tests::common, MoveHarness};
+use aptos_framework::{BuildOptions, BuiltPackage};
 use aptos_package_builder::PackageBuilder;
-use aptos_types::{account_address::AccountAddress, on_chain_config::FeatureFlag};
+use aptos_types::{
+    account_address::AccountAddress, on_chain_config::FeatureFlag, transaction::ExecutionStatus,
+};
+use claims::assert_ok;
 use move_core_types::{language_storage::TypeTag, vm_status::StatusCode};
 use serde::{Deserialize, Serialize};
 use std::str::FromStr;
@@ -116,3 +120,195 @@ fn verify_module_event_upgrades() {
     let result = h.publish_package(&account, path.path());
     assert_vm_status!(result, StatusCode::EVENT_METADATA_VALIDATION_ERROR);
 }
+
+#[test]
+fn test_event_emission_not_allowed_in_scripts() {
+    let mut h = MoveHarness::new();
+    let acc = h.new_account_at(AccountAddress::from_hex_literal("0x123").unwrap());
+    let mut build_options = BuildOptions::move_2().set_latest_language();
+    build_options
+        .experiments
+        .push("skip-bailout-on-extended-checks".to_string());
+
+    let mut builder = PackageBuilder::new("P1");
+    let source = r#"
+    module 0x123::test {
+        #[event]
+        struct Event has copy, drop, store;
+
+        public fun new_event(): Event {
+            Event
+        }
+
+        public fun with_callback(callback: |Event|) {
+            let event = new_event();
+            callback(event);
+        }
+
+        public fun emit_from_callback(callback: ||Event) {
+            let event = callback();
+            0x1::event::emit(event);
+        }
+    }
+    "#;
+    builder.add_source("test.move", source);
+    builder.add_local_dep(
+        "AptosFramework",
+        &common::framework_dir_path("aptos-framework").to_string_lossy(),
+    );
+
+    let p1_path = builder.write_to_temp().unwrap();
+    assert_success!(h.publish_package_with_options(&acc, p1_path.path(), build_options.clone()));
+
+    let mut builder = PackageBuilder::new("P2");
+    let sources = [
+        r#"
+        script {
+            fun main_0() {
+                let event = 0x123::test::new_event();
+                0x1::event::emit<0x123::test::Event>(event);
+            }
+        }
+        "#,
+        r#"
+        script {
+            fun main_1() {
+                let event = 0x123::test::new_event();
+                let f = || 0x1::event::emit<0x123::test::Event>(event);
+                f();
+            }
+        }
+        "#,
+        r#"
+        script {
+            fun main_2() {
+                let f = |e| 0x1::event::emit<0x123::test::Event>(e);
+                0x123::test::with_callback(f);
+            }
+        }
+        "#,
+    ];
+    for (idx, source) in sources.iter().enumerate() {
+        builder.add_source(&format!("main_{idx}.move"), source);
+    }
+    builder.add_local_dep("P1", p1_path.path().to_str().unwrap());
+    builder.add_local_dep(
+        "AptosFramework",
+        &common::framework_dir_path("aptos-framework").to_string_lossy(),
+    );
+
+    let p2_path = builder.write_to_temp().unwrap();
+    let p2_package = BuiltPackage::build(p2_path.path().to_path_buf(), build_options.clone())
+        .expect("Should be able to build a package");
+
+    let scripts = p2_package.extract_script_code();
+    assert_eq!(scripts.len(), 3);
+
+    for script in scripts {
+        let txn = h.create_script(&acc, script, vec![], vec![]);
+        let execution_status = assert_ok!(h.run_raw(txn).status().as_kept_status());
+        assert!(matches!(
+            execution_status,
+            ExecutionStatus::MiscellaneousError(Some(StatusCode::INVALID_OPERATION_IN_SCRIPT))
+        ));
+    }
+
+    let mut builder = PackageBuilder::new("P3");
+    let source = r#"
+        script {
+            fun main() {
+                let f = || 0x123::test::new_event();
+                0x123::test::emit_from_callback(f);
+            }
+        }
+        "#;
+    builder.add_source("main.move", source);
+    builder.add_local_dep("P1", p1_path.path().to_str().unwrap());
+
+    let p3_path = builder.write_to_temp().unwrap();
+    let p3_package = BuiltPackage::build(p3_path.path().to_path_buf(), build_options)
+        .expect("Should be able to build a package");
+
+    let mut scripts = p3_package.extract_script_code();
+    assert_eq!(scripts.len(), 1);
+
+    let txn = h.create_script(&acc, scripts.pop().unwrap(), vec![], vec![]);
+    assert_success!(h.run_raw(txn).status().clone());
+}
+
+#[test]
+fn test_event_emission_in_modules() {
+    let mut h = MoveHarness::new();
+    let acc = h.new_account_at(AccountAddress::from_hex_literal("0x123").unwrap());
+    let mut build_options = BuildOptions::move_2().set_latest_language();
+    build_options
+        .experiments
+        .push("skip-bailout-on-extended-checks".to_string());
+
+    let mut builder = PackageBuilder::new("P1");
+    let source = r#"
+    module 0x123::event {
+        #[event]
+        struct Event has copy, drop, store;
+
+        public fun new_event(): Event {
+            Event
+        }
+    }
+    "#;
+    builder.add_source("event.move", source);
+    let p1_path = builder.write_to_temp().unwrap();
+    assert_success!(h.publish_package_with_options(&acc, p1_path.path(), build_options.clone()));
+
+    let sources = [
+        r#"
+        module 0x123::test2 {
+            public fun emit() {
+                let event = 0x123::event::new_event();
+                0x1::event::emit<0x123::event::Event>(event);
+            }
+        }
+        "#,
+        r#"
+        module 0x123::test3 {
+            public fun emit() {
+                let event = 0x123::event::new_event();
+                let f = || 0x1::event::emit<0x123::event::Event>(event);
+                f();
+            }
+        }
+        "#,
+        r#"
+        module 0x123::test4 {
+            public fun emit() {
+                let event = 0x123::event::new_event();
+                let f = || {
+                  if (true) {
+                    0x1::event::emit<0x123::event::Event>(event);
+                  };
+                };
+                f();
+            }
+        }
+        "#,
+    ];
+    for (idx, source) in sources.into_iter().enumerate() {
+        // To make sure files and packages named consistently.
+        let idx = idx + 2;
+
+        let mut builder = PackageBuilder::new(&format!("P{idx}"));
+        builder.add_source(&format!("test{idx}.move"), source);
+        builder.add_local_dep("P1", p1_path.path().to_str().unwrap());
+        builder.add_local_dep(
+            "AptosFramework",
+            &common::framework_dir_path("aptos-framework").to_string_lossy(),
+        );
+        let path = builder.write_to_temp().unwrap();
+        let status = h.publish_package_with_options(&acc, path.path(), build_options.clone());
+        let execution_status = assert_ok!(status.as_kept_status());
+        assert!(matches!(
+            execution_status,
+            ExecutionStatus::MiscellaneousError(Some(StatusCode::EVENT_METADATA_VALIDATION_ERROR))
+        ));
+    }
+}
diff --git a/aptos-move/framework/aptos-framework/doc/event.md b/aptos-move/framework/aptos-framework/doc/event.md
index ce48f15af29..9dba7bf02fa 100644
--- a/aptos-move/framework/aptos-framework/doc/event.md
+++ b/aptos-move/framework/aptos-framework/doc/event.md
@@ -81,7 +81,7 @@ A handle for an event such that:
 
 <a id="0x1_event_ECANNOT_CREATE_EVENT"></a>
 
-An event cannot be created. This error is returned by native implementations when.
+An event cannot be created. This error is returned by native implementations when
 - The type tag for event is too deeply nested.
 
 
diff --git a/aptos-move/framework/aptos-framework/sources/event.move b/aptos-move/framework/aptos-framework/sources/event.move
index ab322c7af27..0f65378fe4d 100644
--- a/aptos-move/framework/aptos-framework/sources/event.move
+++ b/aptos-move/framework/aptos-framework/sources/event.move
@@ -9,7 +9,7 @@ module aptos_framework::event {
     friend aptos_framework::account;
     friend aptos_framework::object;
 
-    /// An event cannot be created. This error is returned by native implementations when.
+    /// An event cannot be created. This error is returned by native implementations when
     ///   - The type tag for event is too deeply nested.
     const ECANNOT_CREATE_EVENT: u64 = 1;
 
diff --git a/aptos-move/framework/src/natives/event.rs b/aptos-move/framework/src/natives/event.rs
index 8f2d1d1501d..0aeb399ff43 100644
--- a/aptos-move/framework/src/natives/event.rs
+++ b/aptos-move/framework/src/natives/event.rs
@@ -23,7 +23,7 @@ use move_vm_types::{
 use smallvec::{smallvec, SmallVec};
 use std::collections::VecDeque;
 
-/// Error code from events.move, returned when event creation fails.
+/// Error code from `0x1::events.move`, returned when event creation fails.
 pub const ECANNOT_CREATE_EVENT: u64 = 1;
 
 /// Cached emitted module events.
@@ -228,28 +228,36 @@ fn native_write_module_event_to_store(
     let type_tag = context.type_to_type_tag(&ty)?;
 
     // Additional runtime check for module call.
-    if let (Some(id), _, _) = context
-        .stack_frames(1)
+    let stack_frames = context.stack_frames(1);
+    let id = stack_frames
         .stack_trace()
         .first()
+        .map(|(caller, _, _)| caller)
         .ok_or_else(|| {
-            SafeNativeError::InvariantViolation(PartialVMError::new(
-                StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
-            ))
+            let err = PartialVMError::new_invariant_violation(
+                "Caller frame for 0x1::emit::event is not found",
+            );
+            SafeNativeError::InvariantViolation(err)
         })?
-    {
-        if let TypeTag::Struct(ref struct_tag) = type_tag {
-            if id != &struct_tag.module_id() {
-                return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
-                    StatusCode::INTERNAL_TYPE_ERROR,
-                )));
-            }
-        } else {
+        .as_ref()
+        .ok_or_else(|| {
+            // If module is not known, this call must come from the script, which is not allowed.
+            let err = PartialVMError::new_invariant_violation("Scripts cannot emit events");
+            SafeNativeError::InvariantViolation(err)
+        })?;
+
+    if let TypeTag::Struct(ref struct_tag) = type_tag {
+        if id != &struct_tag.module_id() {
             return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
                 StatusCode::INTERNAL_TYPE_ERROR,
             )));
         }
+    } else {
+        return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
+            StatusCode::INTERNAL_TYPE_ERROR,
+        )));
     }
+
     let (layout, has_identifier_mappings) =
         context.type_to_type_layout_with_identifier_mappings(&ty)?;
 
@@ -259,10 +267,9 @@ fn native_write_module_event_to_store(
         .with_func_args_deserialization(&function_value_extension)
         .serialize(&msg, &layout)?
         .ok_or_else(|| {
-            SafeNativeError::InvariantViolation(
-                PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
-                    .with_message("Event serialization failure".to_string()),
-            )
+            SafeNativeError::InvariantViolation(PartialVMError::new_invariant_violation(
+                "Event serialization failure",
+            ))
         })?;
 
     let ctx = context.extensions_mut().get_mut::<NativeEventContext>();


Reviews:
user: login: zi0Black
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T15:30:37Z
user: login: georgemitenkov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T15:33:41Z
user: login: vineethk
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:06:46Z
user: login: zi0Black
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:16:44Z
user: login: zi0Black
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:25:33Z
user: login: zi0Black
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:40:45Z
user: login: georgemitenkov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T10:30:24Z
user: login: georgemitenkov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T10:35:22Z
user: login: georgemitenkov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T10:37:06Z
user: login: georgemitenkov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T10:38:53Z

Review comments:
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
+/// }
+/// ```
 pub(crate) fn validate_emit_calls(
     event_structs: &HashSet<String>,
     module: &CompiledModule,
 ) -> VMResult<()> {
     for fun in module.function_defs() {
         if let Some(code_unit) = &fun.code {
             for bc in &code_unit.code {
-                if let Bytecode::CallGeneric(index) = bc {
-                    let func_instantiation = &module.function_instantiation_at(*index);
-                    let func_handle = module.function_handle_at(func_instantiation.handle);
-                    let module_handle = module.module_handle_at(func_handle.module);
-                    let module_addr = module.address_identifier_at(module_handle.address);
-                    let module_name = module.identifier_at(module_handle.name);
-                    let func_name = module.identifier_at(func_handle.name);
-                    if module_addr != &AccountAddress::ONE
-                        || module_name.as_str() != EVENT_MODULE_NAME
-                        || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
-                    {
-                        continue;
-                    }
-                    let param = module
-                        .signature_at(func_instantiation.type_parameters)
-                        .0
-                        .first()
-                        .ok_or_else(|| {
-                            metadata_validation_error(
-                                "Missing parameter for 0x1::event::emit function",
-                            )
-                        })?;
-                    match param {
-                        StructInstantiation(index, _) | Struct(index) => {
-                            let struct_handle = &module.struct_handle_at(*index);
-                            let struct_name = module.identifier_at(struct_handle.name);
-                            if struct_handle.module != module.self_handle_idx() {
-                                metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
-                            } else if !event_structs.contains(struct_name.as_str()) {
-                                metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
-                            } else {
-                                Ok(())
-                            }
-                        },
-                        _ => metadata_validation_err(
-                            "Passed in a non-struct parameter into 0x1::event::emit.",
-                        ),
-                    }?;
+                use Bytecode::*;
+                match bc {
+                    CallGeneric(index) | PackClosureGeneric(index, ..) => {
+                        let func_instantiation = &module.function_instantiation_at(*index);
+                        let func_handle = module.function_handle_at(func_instantiation.handle);
+                        let module_handle = module.module_handle_at(func_handle.module);
+                        let module_addr = module.address_identifier_at(module_handle.address);
+                        let module_name = module.identifier_at(module_handle.name);
+                        let func_name = module.identifier_at(func_handle.name);
+                        if module_addr != &AccountAddress::ONE
+                            || module_name.as_str() != EVENT_MODULE_NAME
+                            || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
+                        {
+                            continue;
+                        }
+                        let param = module
+                            .signature_at(func_instantiation.type_parameters)
+                            .0
+                            .first()
+                            .ok_or_else(|| {
+                                metadata_validation_error(
+                                    "Missing parameter for 0x1::event::emit function",
+                                )
+                            })?;
+                        match param {
+                            StructInstantiation(index, _) | Struct(index) => {
+                                let struct_handle = &module.struct_handle_at(*index);
+                                let struct_name = module.identifier_at(struct_handle.name);
+                                if struct_handle.module != module.self_handle_idx() {
+                                    metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
+                                } else if !event_structs.contains(struct_name.as_str()) {
+                                    metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
+                                } else {
+                                    Ok(())
+                                }
+                            },
+                            _ => metadata_validation_err(
+                                "Passed in a non-struct parameter into 0x1::event::emit.",
+                            ),
+                        }?;
+                    },
+                    // Note: If a closure is packed, it cannot be 0x1::event::emit, but the lifted
+                    // lambda body may contain the emit function, and so will match the case above.
+                    // For all other instructions, no validation.
+                    PackClosure(_, _)
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: zi0Black
body: nit: `_ => ()` instead of the whole match
created_at: 2025-05-15T15:30:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
+/// }
+/// ```
 pub(crate) fn validate_emit_calls(
     event_structs: &HashSet<String>,
     module: &CompiledModule,
 ) -> VMResult<()> {
     for fun in module.function_defs() {
         if let Some(code_unit) = &fun.code {
             for bc in &code_unit.code {
-                if let Bytecode::CallGeneric(index) = bc {
-                    let func_instantiation = &module.function_instantiation_at(*index);
-                    let func_handle = module.function_handle_at(func_instantiation.handle);
-                    let module_handle = module.module_handle_at(func_handle.module);
-                    let module_addr = module.address_identifier_at(module_handle.address);
-                    let module_name = module.identifier_at(module_handle.name);
-                    let func_name = module.identifier_at(func_handle.name);
-                    if module_addr != &AccountAddress::ONE
-                        || module_name.as_str() != EVENT_MODULE_NAME
-                        || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
-                    {
-                        continue;
-                    }
-                    let param = module
-                        .signature_at(func_instantiation.type_parameters)
-                        .0
-                        .first()
-                        .ok_or_else(|| {
-                            metadata_validation_error(
-                                "Missing parameter for 0x1::event::emit function",
-                            )
-                        })?;
-                    match param {
-                        StructInstantiation(index, _) | Struct(index) => {
-                            let struct_handle = &module.struct_handle_at(*index);
-                            let struct_name = module.identifier_at(struct_handle.name);
-                            if struct_handle.module != module.self_handle_idx() {
-                                metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
-                            } else if !event_structs.contains(struct_name.as_str()) {
-                                metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
-                            } else {
-                                Ok(())
-                            }
-                        },
-                        _ => metadata_validation_err(
-                            "Passed in a non-struct parameter into 0x1::event::emit.",
-                        ),
-                    }?;
+                use Bytecode::*;
+                match bc {
+                    CallGeneric(index) | PackClosureGeneric(index, ..) => {
+                        let func_instantiation = &module.function_instantiation_at(*index);
+                        let func_handle = module.function_handle_at(func_instantiation.handle);
+                        let module_handle = module.module_handle_at(func_handle.module);
+                        let module_addr = module.address_identifier_at(module_handle.address);
+                        let module_name = module.identifier_at(module_handle.name);
+                        let func_name = module.identifier_at(func_handle.name);
+                        if module_addr != &AccountAddress::ONE
+                            || module_name.as_str() != EVENT_MODULE_NAME
+                            || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
+                        {
+                            continue;
+                        }
+                        let param = module
+                            .signature_at(func_instantiation.type_parameters)
+                            .0
+                            .first()
+                            .ok_or_else(|| {
+                                metadata_validation_error(
+                                    "Missing parameter for 0x1::event::emit function",
+                                )
+                            })?;
+                        match param {
+                            StructInstantiation(index, _) | Struct(index) => {
+                                let struct_handle = &module.struct_handle_at(*index);
+                                let struct_name = module.identifier_at(struct_handle.name);
+                                if struct_handle.module != module.self_handle_idx() {
+                                    metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
+                                } else if !event_structs.contains(struct_name.as_str()) {
+                                    metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
+                                } else {
+                                    Ok(())
+                                }
+                            },
+                            _ => metadata_validation_err(
+                                "Passed in a non-struct parameter into 0x1::event::emit.",
+                            ),
+                        }?;
+                    },
+                    // Note: If a closure is packed, it cannot be 0x1::event::emit, but the lifted
+                    // lambda body may contain the emit function, and so will match the case above.
+                    // For all other instructions, no validation.
+                    PackClosure(_, _)
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: georgemitenkov
body: Actually I did a whole match on purpose so next time a bytecode is added we think if this part of the code needs changes. If there was a full match before, this would have been caught during reviews, or at least a thorough reviewer would think about this case and raise any concerns
created_at: 2025-05-15T15:33:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: vineethk
body: This is written under dynamic calls via closures, but this is just a direct call?
created_at: 2025-05-15T15:48:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -139,22 +249,40 @@ pub(crate) fn extract_event_metadata(
     Ok(event_structs)
 }
 
+/// Returns an error if the script uses 0x1::event::emit function (whether as a direct call, or as
+/// a closure). Note that this is not overly restrictive: even a callback to emit an event cannot
+/// be passed, i.e. the following script should fail:
+///
+/// ```move
+/// script {
+///   fun main() {
+///     let f = |e| {
+///       // ... do something here ...
+///       0x1::event::emit(e);
+///     }
+///
+///     // This call creates an event and calls `f` on it.
+///     0x123::some_module::some_function(f);
+///   }
+/// }
+/// ```
+///
+/// This is ok to fail here, as event emission should be done by the module where event is defined.
 pub(crate) fn verify_no_event_emission_in_compiled_script(script: &CompiledScript) -> VMResult<()> {
-    for bc in &script.code().code {
-        if let Bytecode::CallGeneric(index) = bc {
-            let func_instantiation = &script.function_instantiation_at(*index);
-            let func_handle = script.function_handle_at(func_instantiation.handle);
-            let module_handle = script.module_handle_at(func_handle.module);
-            let module_addr = script.address_identifier_at(module_handle.address);
-            let module_name = script.identifier_at(module_handle.name);
-            let func_name = script.identifier_at(func_handle.name);
-            if module_addr == &AccountAddress::ONE
-                && module_name.as_str() == EVENT_MODULE_NAME
-                && func_name.as_str() == EVENT_EMIT_FUNCTION_NAME
-            {
-                return Err(PartialVMError::new(StatusCode::INVALID_OPERATION_IN_SCRIPT)
-                    .finish(Location::Script));
-            }
+    for func_handle in &script.function_handles {
+        let module_handle = script.module_handle_at(func_handle.module);
+
+        let module_addr = script.address_identifier_at(module_handle.address);
+        let module_name = script.identifier_at(module_handle.name);
+        let func_name = script.identifier_at(func_handle.name);
+
+        if module_addr == &AccountAddress::ONE
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: vineethk
body: We can perhaps make a `is_event_emit_function()` and use that here and in `validate_emit_calls()`?
created_at: 2025-05-15T15:57:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -139,22 +249,40 @@ pub(crate) fn extract_event_metadata(
     Ok(event_structs)
 }
 
+/// Returns an error if the script uses 0x1::event::emit function (whether as a direct call, or as
+/// a closure). Note that this is not overly restrictive: even a callback to emit an event cannot
+/// be passed, i.e. the following script should fail:
+///
+/// ```move
+/// script {
+///   fun main() {
+///     let f = |e| {
+///       // ... do something here ...
+///       0x1::event::emit(e);
+///     }
+///
+///     // This call creates an event and calls `f` on it.
+///     0x123::some_module::some_function(f);
+///   }
+/// }
+/// ```
+///
+/// This is ok to fail here, as event emission should be done by the module where event is defined.
 pub(crate) fn verify_no_event_emission_in_compiled_script(script: &CompiledScript) -> VMResult<()> {
-    for bc in &script.code().code {
-        if let Bytecode::CallGeneric(index) = bc {
-            let func_instantiation = &script.function_instantiation_at(*index);
-            let func_handle = script.function_handle_at(func_instantiation.handle);
-            let module_handle = script.module_handle_at(func_handle.module);
-            let module_addr = script.address_identifier_at(module_handle.address);
-            let module_name = script.identifier_at(module_handle.name);
-            let func_name = script.identifier_at(func_handle.name);
-            if module_addr == &AccountAddress::ONE
-                && module_name.as_str() == EVENT_MODULE_NAME
-                && func_name.as_str() == EVENT_EMIT_FUNCTION_NAME
-            {
-                return Err(PartialVMError::new(StatusCode::INVALID_OPERATION_IN_SCRIPT)
-                    .finish(Location::Script));
-            }
+    for func_handle in &script.function_handles {
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: vineethk
body: Nice, looks like instead of scanning the code in the script, you accomplish the same effect (and also cover closures) by looking at loaded external functions.
created_at: 2025-05-15T16:00:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
+/// }
+/// ```
 pub(crate) fn validate_emit_calls(
     event_structs: &HashSet<String>,
     module: &CompiledModule,
 ) -> VMResult<()> {
     for fun in module.function_defs() {
         if let Some(code_unit) = &fun.code {
             for bc in &code_unit.code {
-                if let Bytecode::CallGeneric(index) = bc {
-                    let func_instantiation = &module.function_instantiation_at(*index);
-                    let func_handle = module.function_handle_at(func_instantiation.handle);
-                    let module_handle = module.module_handle_at(func_handle.module);
-                    let module_addr = module.address_identifier_at(module_handle.address);
-                    let module_name = module.identifier_at(module_handle.name);
-                    let func_name = module.identifier_at(func_handle.name);
-                    if module_addr != &AccountAddress::ONE
-                        || module_name.as_str() != EVENT_MODULE_NAME
-                        || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
-                    {
-                        continue;
-                    }
-                    let param = module
-                        .signature_at(func_instantiation.type_parameters)
-                        .0
-                        .first()
-                        .ok_or_else(|| {
-                            metadata_validation_error(
-                                "Missing parameter for 0x1::event::emit function",
-                            )
-                        })?;
-                    match param {
-                        StructInstantiation(index, _) | Struct(index) => {
-                            let struct_handle = &module.struct_handle_at(*index);
-                            let struct_name = module.identifier_at(struct_handle.name);
-                            if struct_handle.module != module.self_handle_idx() {
-                                metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
-                            } else if !event_structs.contains(struct_name.as_str()) {
-                                metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
-                            } else {
-                                Ok(())
-                            }
-                        },
-                        _ => metadata_validation_err(
-                            "Passed in a non-struct parameter into 0x1::event::emit.",
-                        ),
-                    }?;
+                use Bytecode::*;
+                match bc {
+                    CallGeneric(index) | PackClosureGeneric(index, ..) => {
+                        let func_instantiation = &module.function_instantiation_at(*index);
+                        let func_handle = module.function_handle_at(func_instantiation.handle);
+                        let module_handle = module.module_handle_at(func_handle.module);
+                        let module_addr = module.address_identifier_at(module_handle.address);
+                        let module_name = module.identifier_at(module_handle.name);
+                        let func_name = module.identifier_at(func_handle.name);
+                        if module_addr != &AccountAddress::ONE
+                            || module_name.as_str() != EVENT_MODULE_NAME
+                            || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
+                        {
+                            continue;
+                        }
+                        let param = module
+                            .signature_at(func_instantiation.type_parameters)
+                            .0
+                            .first()
+                            .ok_or_else(|| {
+                                metadata_validation_error(
+                                    "Missing parameter for 0x1::event::emit function",
+                                )
+                            })?;
+                        match param {
+                            StructInstantiation(index, _) | Struct(index) => {
+                                let struct_handle = &module.struct_handle_at(*index);
+                                let struct_name = module.identifier_at(struct_handle.name);
+                                if struct_handle.module != module.self_handle_idx() {
+                                    metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
+                                } else if !event_structs.contains(struct_name.as_str()) {
+                                    metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
+                                } else {
+                                    Ok(())
+                                }
+                            },
+                            _ => metadata_validation_err(
+                                "Passed in a non-struct parameter into 0x1::event::emit.",
+                            ),
+                        }?;
+                    },
+                    // Note: If a closure is packed, it cannot be 0x1::event::emit, but the lifted
+                    // lambda body may contain the emit function, and so will match the case above.
+                    // For all other instructions, no validation.
+                    PackClosure(_, _)
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: vineethk
body: +1 for not having `_ =>`.
created_at: 2025-05-15T16:06:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
+/// }
+/// ```
 pub(crate) fn validate_emit_calls(
     event_structs: &HashSet<String>,
     module: &CompiledModule,
 ) -> VMResult<()> {
     for fun in module.function_defs() {
         if let Some(code_unit) = &fun.code {
             for bc in &code_unit.code {
-                if let Bytecode::CallGeneric(index) = bc {
-                    let func_instantiation = &module.function_instantiation_at(*index);
-                    let func_handle = module.function_handle_at(func_instantiation.handle);
-                    let module_handle = module.module_handle_at(func_handle.module);
-                    let module_addr = module.address_identifier_at(module_handle.address);
-                    let module_name = module.identifier_at(module_handle.name);
-                    let func_name = module.identifier_at(func_handle.name);
-                    if module_addr != &AccountAddress::ONE
-                        || module_name.as_str() != EVENT_MODULE_NAME
-                        || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
-                    {
-                        continue;
-                    }
-                    let param = module
-                        .signature_at(func_instantiation.type_parameters)
-                        .0
-                        .first()
-                        .ok_or_else(|| {
-                            metadata_validation_error(
-                                "Missing parameter for 0x1::event::emit function",
-                            )
-                        })?;
-                    match param {
-                        StructInstantiation(index, _) | Struct(index) => {
-                            let struct_handle = &module.struct_handle_at(*index);
-                            let struct_name = module.identifier_at(struct_handle.name);
-                            if struct_handle.module != module.self_handle_idx() {
-                                metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
-                            } else if !event_structs.contains(struct_name.as_str()) {
-                                metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
-                            } else {
-                                Ok(())
-                            }
-                        },
-                        _ => metadata_validation_err(
-                            "Passed in a non-struct parameter into 0x1::event::emit.",
-                        ),
-                    }?;
+                use Bytecode::*;
+                match bc {
+                    CallGeneric(index) | PackClosureGeneric(index, ..) => {
+                        let func_instantiation = &module.function_instantiation_at(*index);
+                        let func_handle = module.function_handle_at(func_instantiation.handle);
+                        let module_handle = module.module_handle_at(func_handle.module);
+                        let module_addr = module.address_identifier_at(module_handle.address);
+                        let module_name = module.identifier_at(module_handle.name);
+                        let func_name = module.identifier_at(func_handle.name);
+                        if module_addr != &AccountAddress::ONE
+                            || module_name.as_str() != EVENT_MODULE_NAME
+                            || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
+                        {
+                            continue;
+                        }
+                        let param = module
+                            .signature_at(func_instantiation.type_parameters)
+                            .0
+                            .first()
+                            .ok_or_else(|| {
+                                metadata_validation_error(
+                                    "Missing parameter for 0x1::event::emit function",
+                                )
+                            })?;
+                        match param {
+                            StructInstantiation(index, _) | Struct(index) => {
+                                let struct_handle = &module.struct_handle_at(*index);
+                                let struct_name = module.identifier_at(struct_handle.name);
+                                if struct_handle.module != module.self_handle_idx() {
+                                    metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
+                                } else if !event_structs.contains(struct_name.as_str()) {
+                                    metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
+                                } else {
+                                    Ok(())
+                                }
+                            },
+                            _ => metadata_validation_err(
+                                "Passed in a non-struct parameter into 0x1::event::emit.",
+                            ),
+                        }?;
+                    },
+                    // Note: If a closure is packed, it cannot be 0x1::event::emit, but the lifted
+                    // lambda body may contain the emit function, and so will match the case above.
+                    // For all other instructions, no validation.
+                    PackClosure(_, _)
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: zi0Black
body: Can this be a comment?
> This full match has been left on purses to evaluate all ByteCode instructions in case new ones are added.
created_at: 2025-05-15T16:16:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -228,28 +228,35 @@ fn native_write_module_event_to_store(
     let type_tag = context.type_to_type_tag(&ty)?;
 
     // Additional runtime check for module call.
-    if let (Some(id), _, _) = context
-        .stack_frames(1)
+    let stack_frames = context.stack_frames(1);
+    let id = stack_frames
         .stack_trace()
         .first()
+        .map(|(caller, _, _)| caller)
         .ok_or_else(|| {
             SafeNativeError::InvariantViolation(PartialVMError::new(
path: aptos-move/framework/src/natives/event.rs
user: login: zi0Black
body: Could we get some messages here to help us tell the PartialVMError apart?
created_at: 2025-05-15T16:25:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
+/// }
+/// ```
 pub(crate) fn validate_emit_calls(
     event_structs: &HashSet<String>,
     module: &CompiledModule,
 ) -> VMResult<()> {
     for fun in module.function_defs() {
         if let Some(code_unit) = &fun.code {
             for bc in &code_unit.code {
-                if let Bytecode::CallGeneric(index) = bc {
-                    let func_instantiation = &module.function_instantiation_at(*index);
-                    let func_handle = module.function_handle_at(func_instantiation.handle);
-                    let module_handle = module.module_handle_at(func_handle.module);
-                    let module_addr = module.address_identifier_at(module_handle.address);
-                    let module_name = module.identifier_at(module_handle.name);
-                    let func_name = module.identifier_at(func_handle.name);
-                    if module_addr != &AccountAddress::ONE
-                        || module_name.as_str() != EVENT_MODULE_NAME
-                        || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
-                    {
-                        continue;
-                    }
-                    let param = module
-                        .signature_at(func_instantiation.type_parameters)
-                        .0
-                        .first()
-                        .ok_or_else(|| {
-                            metadata_validation_error(
-                                "Missing parameter for 0x1::event::emit function",
-                            )
-                        })?;
-                    match param {
-                        StructInstantiation(index, _) | Struct(index) => {
-                            let struct_handle = &module.struct_handle_at(*index);
-                            let struct_name = module.identifier_at(struct_handle.name);
-                            if struct_handle.module != module.self_handle_idx() {
-                                metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
-                            } else if !event_structs.contains(struct_name.as_str()) {
-                                metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
-                            } else {
-                                Ok(())
-                            }
-                        },
-                        _ => metadata_validation_err(
-                            "Passed in a non-struct parameter into 0x1::event::emit.",
-                        ),
-                    }?;
+                use Bytecode::*;
+                match bc {
+                    CallGeneric(index) | PackClosureGeneric(index, ..) => {
+                        let func_instantiation = &module.function_instantiation_at(*index);
+                        let func_handle = module.function_handle_at(func_instantiation.handle);
+                        let module_handle = module.module_handle_at(func_handle.module);
+                        let module_addr = module.address_identifier_at(module_handle.address);
+                        let module_name = module.identifier_at(module_handle.name);
+                        let func_name = module.identifier_at(func_handle.name);
+                        if module_addr != &AccountAddress::ONE
+                            || module_name.as_str() != EVENT_MODULE_NAME
+                            || func_name.as_str() != EVENT_EMIT_FUNCTION_NAME
+                        {
+                            continue;
+                        }
+                        let param = module
+                            .signature_at(func_instantiation.type_parameters)
+                            .0
+                            .first()
+                            .ok_or_else(|| {
+                                metadata_validation_error(
+                                    "Missing parameter for 0x1::event::emit function",
+                                )
+                            })?;
+                        match param {
+                            StructInstantiation(index, _) | Struct(index) => {
+                                let struct_handle = &module.struct_handle_at(*index);
+                                let struct_name = module.identifier_at(struct_handle.name);
+                                if struct_handle.module != module.self_handle_idx() {
+                                    metadata_validation_err(format!("{} passed to 0x1::event::emit function is not defined in the same module", struct_name).as_str())
+                                } else if !event_structs.contains(struct_name.as_str()) {
+                                    metadata_validation_err(format!("Missing #[event] attribute on {}. The #[event] attribute is required for all structs passed into 0x1::event::emit.", struct_name).as_str())
+                                } else {
+                                    Ok(())
+                                }
+                            },
+                            _ => metadata_validation_err(
+                                "Passed in a non-struct parameter into 0x1::event::emit.",
+                            ),
+                        }?;
+                    },
+                    // Note: If a closure is packed, it cannot be 0x1::event::emit, but the lifted
+                    // lambda body may contain the emit function, and so will match the case above.
+                    // For all other instructions, no validation.
+                    PackClosure(_, _)
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: georgemitenkov
body: Indeed, added a comment 
created_at: 2025-05-16T10:30:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -139,22 +249,40 @@ pub(crate) fn extract_event_metadata(
     Ok(event_structs)
 }
 
+/// Returns an error if the script uses 0x1::event::emit function (whether as a direct call, or as
+/// a closure). Note that this is not overly restrictive: even a callback to emit an event cannot
+/// be passed, i.e. the following script should fail:
+///
+/// ```move
+/// script {
+///   fun main() {
+///     let f = |e| {
+///       // ... do something here ...
+///       0x1::event::emit(e);
+///     }
+///
+///     // This call creates an event and calls `f` on it.
+///     0x123::some_module::some_function(f);
+///   }
+/// }
+/// ```
+///
+/// This is ok to fail here, as event emission should be done by the module where event is defined.
 pub(crate) fn verify_no_event_emission_in_compiled_script(script: &CompiledScript) -> VMResult<()> {
-    for bc in &script.code().code {
-        if let Bytecode::CallGeneric(index) = bc {
-            let func_instantiation = &script.function_instantiation_at(*index);
-            let func_handle = script.function_handle_at(func_instantiation.handle);
-            let module_handle = script.module_handle_at(func_handle.module);
-            let module_addr = script.address_identifier_at(module_handle.address);
-            let module_name = script.identifier_at(module_handle.name);
-            let func_name = script.identifier_at(func_handle.name);
-            if module_addr == &AccountAddress::ONE
-                && module_name.as_str() == EVENT_MODULE_NAME
-                && func_name.as_str() == EVENT_EMIT_FUNCTION_NAME
-            {
-                return Err(PartialVMError::new(StatusCode::INVALID_OPERATION_IN_SCRIPT)
-                    .finish(Location::Script));
-            }
+    for func_handle in &script.function_handles {
+        let module_handle = script.module_handle_at(func_handle.module);
+
+        let module_addr = script.address_identifier_at(module_handle.address);
+        let module_name = script.identifier_at(module_handle.name);
+        let func_name = script.identifier_at(func_handle.name);
+
+        if module_addr == &AccountAddress::ONE
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: georgemitenkov
body: done
created_at: 2025-05-16T10:35:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -71,52 +71,162 @@ pub(crate) fn validate_module_events(
 }
 
 /// Validate all the `0x1::event::emit` calls have the struct defined in the same module with event
-/// attribute.
+/// attribute. Note that this function checks static calls, e.g.
+///
+/// ```move
+/// // Here, `Event` must be defined in the same module.
+/// 0x1::event::emit<Event>();
+/// ```
+///
+/// as well as dynamic calls via closures:
+///
+/// ```move
+/// // Both are allowed only if `Event` is defined in the same module.
+/// let f = || 0x1::event::emit<Event>();
+/// let g = {
+///    // ... some Move code ...
+///    0x1::event::emit<Event>();
path: aptos-move/aptos-vm/src/verifier/event_validation.rs
user: login: georgemitenkov
body: Changed the comment from static to regular and removed dynamic: what i wanted to say in the comment is that if closure calls emit, it is still checked (lifted lambda is a function definition)
created_at: 2025-05-16T10:37:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -228,28 +228,35 @@ fn native_write_module_event_to_store(
     let type_tag = context.type_to_type_tag(&ty)?;
 
     // Additional runtime check for module call.
-    if let (Some(id), _, _) = context
-        .stack_frames(1)
+    let stack_frames = context.stack_frames(1);
+    let id = stack_frames
         .stack_trace()
         .first()
+        .map(|(caller, _, _)| caller)
         .ok_or_else(|| {
             SafeNativeError::InvariantViolation(PartialVMError::new(
path: aptos-move/framework/src/natives/event.rs
user: login: georgemitenkov
body: Added messages, though they will be removed when converting to VM status :( But at lest for local debugging should help 👍
created_at: 2025-05-16T10:38:53Z
author_association: CONTRIBUTOR

PR comments:
user: login: georgemitenkov
created_at: 2025-05-15T14:25:31Z
body: * **#16638** <a href="https://app.graphite.dev/github/pr/aptos-labs/aptos-core/16638?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/aptos-labs/aptos-core/16638?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* **#16636** <a href="https://app.graphite.dev/github/pr/aptos-labs/aptos-core/16636?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#16634** <a href="https://app.graphite.dev/github/pr/aptos-labs/aptos-core/16634?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T15:40:17Z
body: ### Forge is running suite `realistic_env_max_load` on `424d541b3406a46d667725389acefb481efdefe7`
* [Grafana dashboard (auto-refresh)](https://aptoslabs.grafana.net/d/overview/overview?orgId=1&refresh=10s&var-Datasource=VictoriaMetrics%20Global%20%28Non-mainnet%29&var-BigQuery=Google%20BigQuery&var-namespace=forge-e2e-pr-16638&var-metrics_source=All&var-chain_name=forge-0&refresh=10s&from=now-15m&to=now)
* [Humio Logs](https://cloud.us.humio.com/k8s/search?query=%24forgeLogs%28validator_instance%3D%2A%29+%7C%0A++++%22k8s.namespace%22+%3D+%22forge-e2e-pr-16638%22+%2F%2F+filters+on+namespace+which+contains+validator+logs%0A+++OR++%2F%2F+remove+either+side+of+the+OR+operator+to+only+display+validator+or+forge-runner+logs%0A++++%28%22k8s.namespace%22%3Ddefault+AND+%22k8s.labels.forge-namespace%22+%3D+%22forge-e2e-pr-16638%22%29+%2F%2F+filters+on+specific+forge-runner+pod+in+default+namespace%0A&widgetType=list-view&columns=%5B%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22%40timestamp%22%2C+%22format%22%3A+%22timestamp%22%2C+%22width%22%3A+180%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22level%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+54%7D%2C+%7B%22type%22%3A+%22link%22%2C+%22openInNewBrowserTab%22%3A+%22%2A%2A%2A%22%2C+%22style%22%3A+%22button%22%2C+%22hrefTemplate%22%3A+%22https%3A%2F%2Fgithub.com%2Faptos-labs%2Faptos-core%2Fpull%2F%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22textTemplate%22%3A+%22%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22header%22%3A+%22Forge+PR%22%2C+%22width%22%3A+79%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.namespace%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+104%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.pod_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+126%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.container_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+85%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22message%22%2C+%22format%22%3A+%22text%22%7D%5D&newestAtBottom=%2A%2A%2A&showOnlyFirstLine=false&live=true&start=30m)
* [Axiom Logs](https://app.axiom.co/aptoslabs-hghf/explorer?initForm=%7B%22apl%22%3A%20%22%5Cn%20%20%20%20%20%20%20%20k8s%5Cn%20%20%20%20%20%20%20%20%7C%20where%20%5B%27k8s.cluster%27%5D%20contains%20%5C%22forge%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22calico-node%5C%22%20and%20%5B%27k8s.namespace%27%5D%20%21%3D%20%5C%22calico-apiserver%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22kube-proxy%5C%22%20and%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%28%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.namespace%27%5D%20%3D%3D%20%5C%22forge-e2e-pr-16638%5C%22%20//%20filters%20on%20namespace%20which%20contains%20validator%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20or%20//%20remove%20either%20side%20of%20the%20OR%20operator%20to%20only%20display%20validator%20or%20forge-runner%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.labels.forge-namespace%27%5D%20%3D%3D%20%5C%22forge-e2e-pr-16638%5C%22%20//%20filters%20on%20specific%20forge-runner%20pod%20in%20default%20namespace%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%29%5Cn%20%20%20%20%20%20%20%20%22%2C%20%22queryOptions%22%3A%20%7B%22quickRange%22%3A%20%2230m%22%7D%7D)
* [Validator CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=now-1h&until=now&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-e2e-pr-16638&var-filters=pod%7C%3D~%7C.%2Avalidator.%2A)
* [Fullnode CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=now-1h&until=now&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-e2e-pr-16638&var-filters=pod%7C%3D~%7C.%2Afullnode.%2A)
* [Test runner output](https://github.com/aptos-labs/aptos-core/actions/runs/15213747590)
* Test run is land-blocking
<!-- Sticky Pull Request Commentforge-e2e -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T15:40:18Z
body: ### Forge is running suite `compat` on `6ff41a58d52e09e11cf6d88c770b376f1108749d` ==> `424d541b3406a46d667725389acefb481efdefe7`
* [Grafana dashboard (auto-refresh)](https://aptoslabs.grafana.net/d/overview/overview?orgId=1&refresh=10s&var-Datasource=VictoriaMetrics%20Global%20%28Non-mainnet%29&var-BigQuery=Google%20BigQuery&var-namespace=forge-compat-pr-16638&var-metrics_source=All&var-chain_name=forge-1&refresh=10s&from=now-15m&to=now)
* [Humio Logs](https://cloud.us.humio.com/k8s/search?query=%24forgeLogs%28validator_instance%3D%2A%29+%7C%0A++++%22k8s.namespace%22+%3D+%22forge-compat-pr-16638%22+%2F%2F+filters+on+namespace+which+contains+validator+logs%0A+++OR++%2F%2F+remove+either+side+of+the+OR+operator+to+only+display+validator+or+forge-runner+logs%0A++++%28%22k8s.namespace%22%3Ddefault+AND+%22k8s.labels.forge-namespace%22+%3D+%22forge-compat-pr-16638%22%29+%2F%2F+filters+on+specific+forge-runner+pod+in+default+namespace%0A&widgetType=list-view&columns=%5B%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22%40timestamp%22%2C+%22format%22%3A+%22timestamp%22%2C+%22width%22%3A+180%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22level%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+54%7D%2C+%7B%22type%22%3A+%22link%22%2C+%22openInNewBrowserTab%22%3A+%22%2A%2A%2A%22%2C+%22style%22%3A+%22button%22%2C+%22hrefTemplate%22%3A+%22https%3A%2F%2Fgithub.com%2Faptos-labs%2Faptos-core%2Fpull%2F%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22textTemplate%22%3A+%22%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22header%22%3A+%22Forge+PR%22%2C+%22width%22%3A+79%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.namespace%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+104%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.pod_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+126%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.container_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+85%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22message%22%2C+%22format%22%3A+%22text%22%7D%5D&newestAtBottom=%2A%2A%2A&showOnlyFirstLine=false&live=true&start=30m)
* [Axiom Logs](https://app.axiom.co/aptoslabs-hghf/explorer?initForm=%7B%22apl%22%3A%20%22%5Cn%20%20%20%20%20%20%20%20k8s%5Cn%20%20%20%20%20%20%20%20%7C%20where%20%5B%27k8s.cluster%27%5D%20contains%20%5C%22forge%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22calico-node%5C%22%20and%20%5B%27k8s.namespace%27%5D%20%21%3D%20%5C%22calico-apiserver%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22kube-proxy%5C%22%20and%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%28%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.namespace%27%5D%20%3D%3D%20%5C%22forge-compat-pr-16638%5C%22%20//%20filters%20on%20namespace%20which%20contains%20validator%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20or%20//%20remove%20either%20side%20of%20the%20OR%20operator%20to%20only%20display%20validator%20or%20forge-runner%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.labels.forge-namespace%27%5D%20%3D%3D%20%5C%22forge-compat-pr-16638%5C%22%20//%20filters%20on%20specific%20forge-runner%20pod%20in%20default%20namespace%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%29%5Cn%20%20%20%20%20%20%20%20%22%2C%20%22queryOptions%22%3A%20%7B%22quickRange%22%3A%20%2230m%22%7D%7D)
* [Validator CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=now-1h&until=now&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-compat-pr-16638&var-filters=pod%7C%3D~%7C.%2Avalidator.%2A)
* [Fullnode CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=now-1h&until=now&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-compat-pr-16638&var-filters=pod%7C%3D~%7C.%2Afullnode.%2A)
* [Test runner output](https://github.com/aptos-labs/aptos-core/actions/runs/15213747590)
* Test run is land-blocking
<!-- Sticky Pull Request Commentforge-compat -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T15:40:19Z
body: ### Forge is running suite `framework_upgrade` on `6ff41a58d52e09e11cf6d88c770b376f1108749d` ==> `424d541b3406a46d667725389acefb481efdefe7`
* [Grafana dashboard (auto-refresh)](https://aptoslabs.grafana.net/d/overview/overview?orgId=1&refresh=10s&var-Datasource=VictoriaMetrics%20Global%20%28Non-mainnet%29&var-BigQuery=Google%20BigQuery&var-namespace=forge-framework-upgrade-pr-16638&var-metrics_source=All&var-chain_name=forge-0&refresh=10s&from=now-15m&to=now)
* [Humio Logs](https://cloud.us.humio.com/k8s/search?query=%24forgeLogs%28validator_instance%3D%2A%29+%7C%0A++++%22k8s.namespace%22+%3D+%22forge-framework-upgrade-pr-16638%22+%2F%2F+filters+on+namespace+which+contains+validator+logs%0A+++OR++%2F%2F+remove+either+side+of+the+OR+operator+to+only+display+validator+or+forge-runner+logs%0A++++%28%22k8s.namespace%22%3Ddefault+AND+%22k8s.labels.forge-namespace%22+%3D+%22forge-framework-upgrade-pr-16638%22%29+%2F%2F+filters+on+specific+forge-runner+pod+in+default+namespace%0A&widgetType=list-view&columns=%5B%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22%40timestamp%22%2C+%22format%22%3A+%22timestamp%22%2C+%22width%22%3A+180%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22level%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+54%7D%2C+%7B%22type%22%3A+%22link%22%2C+%22openInNewBrowserTab%22%3A+%22%2A%2A%2A%22%2C+%22style%22%3A+%22button%22%2C+%22hrefTemplate%22%3A+%22https%3A%2F%2Fgithub.com%2Faptos-labs%2Faptos-core%2Fpull%2F%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22textTemplate%22%3A+%22%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22header%22%3A+%22Forge+PR%22%2C+%22width%22%3A+79%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.namespace%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+104%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.pod_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+126%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.container_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+85%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22message%22%2C+%22format%22%3A+%22text%22%7D%5D&newestAtBottom=%2A%2A%2A&showOnlyFirstLine=false&live=true&start=30m)
* [Axiom Logs](https://app.axiom.co/aptoslabs-hghf/explorer?initForm=%7B%22apl%22%3A%20%22%5Cn%20%20%20%20%20%20%20%20k8s%5Cn%20%20%20%20%20%20%20%20%7C%20where%20%5B%27k8s.cluster%27%5D%20contains%20%5C%22forge%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22calico-node%5C%22%20and%20%5B%27k8s.namespace%27%5D%20%21%3D%20%5C%22calico-apiserver%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22kube-proxy%5C%22%20and%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%28%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.namespace%27%5D%20%3D%3D%20%5C%22forge-framework-upgrade-pr-16638%5C%22%20//%20filters%20on%20namespace%20which%20contains%20validator%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20or%20//%20remove%20either%20side%20of%20the%20OR%20operator%20to%20only%20display%20validator%20or%20forge-runner%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.labels.forge-namespace%27%5D%20%3D%3D%20%5C%22forge-framework-upgrade-pr-16638%5C%22%20//%20filters%20on%20specific%20forge-runner%20pod%20in%20default%20namespace%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%29%5Cn%20%20%20%20%20%20%20%20%22%2C%20%22queryOptions%22%3A%20%7B%22quickRange%22%3A%20%2230m%22%7D%7D)
* [Validator CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=now-1h&until=now&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-framework-upgrade-pr-16638&var-filters=pod%7C%3D~%7C.%2Avalidator.%2A)
* [Fullnode CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=now-1h&until=now&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-framework-upgrade-pr-16638&var-filters=pod%7C%3D~%7C.%2Afullnode.%2A)
* [Test runner output](https://github.com/aptos-labs/aptos-core/actions/runs/15213747590)
* Test run is land-blocking
<!-- Sticky Pull Request Commentforge-framework-upgrade -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T15:51:02Z
body: ### :white_check_mark: Forge suite `compat` success on `6ff41a58d52e09e11cf6d88c770b376f1108749d` ==> `424d541b3406a46d667725389acefb481efdefe7`
```
Compatibility test results for 6ff41a58d52e09e11cf6d88c770b376f1108749d ==> 424d541b3406a46d667725389acefb481efdefe7 (PR)
1. Check liveness of validators at old version: 6ff41a58d52e09e11cf6d88c770b376f1108749d
compatibility::simple-validator-upgrade::liveness-check : committed: 8949.32 txn/s, latency: 3812.40 ms, (p50: 4200 ms, p70: 4300, p90: 4400 ms, p99: 4600 ms), latency samples: 295280
2. Upgrading first Validator to new version: 424d541b3406a46d667725389acefb481efdefe7
compatibility::simple-validator-upgrade::single-validator-upgrade : committed: 2726.75 txn/s, latency: 12110.74 ms, (p50: 13600 ms, p70: 14200, p90: 14300 ms, p99: 14500 ms), latency samples: 103380
3. Upgrading rest of first batch to new version: 424d541b3406a46d667725389acefb481efdefe7
compatibility::simple-validator-upgrade::half-validator-upgrade : committed: 2524.89 txn/s, latency: 13303.10 ms, (p50: 14700 ms, p70: 15300, p90: 15600 ms, p99: 15700 ms), latency samples: 94340
4. upgrading second batch to new version: 424d541b3406a46d667725389acefb481efdefe7
compatibility::simple-validator-upgrade::rest-validator-upgrade : committed: 4754.82 txn/s, latency: 7207.83 ms, (p50: 7700 ms, p70: 8100, p90: 8700 ms, p99: 9000 ms), latency samples: 167660
5. check swarm health
Compatibility test for 6ff41a58d52e09e11cf6d88c770b376f1108749d ==> 424d541b3406a46d667725389acefb481efdefe7 passed
Test Ok
```
* [Grafana dashboard](https://aptoslabs.grafana.net/d/overview/overview?orgId=1&refresh=10s&var-Datasource=VictoriaMetrics%20Global%20%28Non-mainnet%29&var-BigQuery=Google%20BigQuery&var-namespace=forge-compat-pr-16638&var-metrics_source=All&var-chain_name=forge-1&from=1748014825000&to=1748015460000)
* [Humio Logs](https://cloud.us.humio.com/k8s/search?query=%24forgeLogs%28validator_instance%3D%2A%29+%7C%0A++++%22k8s.namespace%22+%3D+%22forge-compat-pr-16638%22+%2F%2F+filters+on+namespace+which+contains+validator+logs%0A+++OR++%2F%2F+remove+either+side+of+the+OR+operator+to+only+display+validator+or+forge-runner+logs%0A++++%28%22k8s.namespace%22%3Ddefault+AND+%22k8s.labels.forge-namespace%22+%3D+%22forge-compat-pr-16638%22%29+%2F%2F+filters+on+specific+forge-runner+pod+in+default+namespace%0A&widgetType=list-view&columns=%5B%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22%40timestamp%22%2C+%22format%22%3A+%22timestamp%22%2C+%22width%22%3A+180%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22level%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+54%7D%2C+%7B%22type%22%3A+%22link%22%2C+%22openInNewBrowserTab%22%3A+%22%2A%2A%2A%22%2C+%22style%22%3A+%22button%22%2C+%22hrefTemplate%22%3A+%22https%3A%2F%2Fgithub.com%2Faptos-labs%2Faptos-core%2Fpull%2F%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22textTemplate%22%3A+%22%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22header%22%3A+%22Forge+PR%22%2C+%22width%22%3A+79%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.namespace%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+104%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.pod_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+126%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.container_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+85%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22message%22%2C+%22format%22%3A+%22text%22%7D%5D&newestAtBottom=%2A%2A%2A&showOnlyFirstLine=false&live=false&start=1748014825000&end=1748015460000)
* [Axiom Logs](https://app.axiom.co/aptoslabs-hghf/explorer?initForm=%7B%22apl%22%3A%20%22%5Cn%20%20%20%20%20%20%20%20k8s%5Cn%20%20%20%20%20%20%20%20%7C%20where%20%5B%27k8s.cluster%27%5D%20contains%20%5C%22forge%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22calico-node%5C%22%20and%20%5B%27k8s.namespace%27%5D%20%21%3D%20%5C%22calico-apiserver%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22kube-proxy%5C%22%20and%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%28%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.namespace%27%5D%20%3D%3D%20%5C%22forge-compat-pr-16638%5C%22%20//%20filters%20on%20namespace%20which%20contains%20validator%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20or%20//%20remove%20either%20side%20of%20the%20OR%20operator%20to%20only%20display%20validator%20or%20forge-runner%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.labels.forge-namespace%27%5D%20%3D%3D%20%5C%22forge-compat-pr-16638%5C%22%20//%20filters%20on%20specific%20forge-runner%20pod%20in%20default%20namespace%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%29%5Cn%20%20%20%20%20%20%20%20%22%2C%20%22queryOptions%22%3A%20%7B%22startTime%22%3A%20%222025-05-23T15%3A40%3A25.626307%2B00%3A00%22%2C%20%22endTime%22%3A%20%222025-05-23T15%3A51%3A00.971552%2B00%3A00%22%7D%7D)
* [Validator CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=1748014825&until=1748015460&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-compat-pr-16638&var-filters=pod%7C%3D~%7C.%2Avalidator.%2A)
* [Fullnode CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=1748014825&until=1748015460&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-compat-pr-16638&var-filters=pod%7C%3D~%7C.%2Afullnode.%2A)
* [Test runner output](https://github.com/aptos-labs/aptos-core/actions/runs/15213747590)
* Test run is land-blocking
<!-- Sticky Pull Request Commentforge-compat -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T15:52:01Z
body: ### :white_check_mark: Forge suite `realistic_env_max_load` success on `424d541b3406a46d667725389acefb481efdefe7`
```
Forge report malformed: Expecting property name enclosed in double quotes: line 5 column 1 (char 78)
'{\n  "metrics": [\n    {\n      "test_name": "two traffics test: inner traffic",\n[2025-05-23T15:51:57Z INFO  aptos_forge::report] Test Ok\n      "metric": "submitted_txn",\n      "value": 4213900.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "expired_txn",\n      "value": 1805.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "avg_tps",\n      "value": 11078.038625485522\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "avg_latency",\n      "value": 3507.987470368071\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "p50_latency",\n      "value": 2900.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "p90_latency",\n      "value": 3400.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "p99_latency",\n      "value": 28200.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "submitted_txn",\n      "value": 43840.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "expired_txn",\n      "value": 0.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "avg_tps",\n      "value": 99.97214639178333\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "avg_latency",\n      "value": 1307.6289156626506\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "p50_latency",\n      "value": 1000.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "p90_latency",\n      "value": 2300.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "p99_latency",\n      "value": 3600.0\n    }\n  ],\n  "text": "two traffics test: inner traffic : committed: 11078.04 txn/s, submitted: 11082.79 txn/s, expired: 4.75 txn/s, latency: 3507.99 ms, (p50: 2900 ms, p70: 3000, p90: 3400 ms, p99: 28200 ms), latency samples: 4212095\\ntwo traffics test : committed: 99.97 txn/s, latency: 1307.63 ms, (p50: 1000 ms, p70: 1300, p90: 2300 ms, p99: 3600 ms), latency samples: 1660\\nLatency breakdown for phase 0: [\\"MempoolToBlockCreation: max: 2.718, avg: 2.468\\", \\"ConsensusProposalToOrdered: max: 0.196, avg: 0.190\\", \\"ConsensusOrderedToCommit: max: 0.242, avg: 0.229\\", \\"ConsensusProposalToCommit: max: 0.436, avg: 0.420\\"]\\nMax non-epoch-change gap was: 1 rounds at version 758693 (avg 0.00) [limit 4], 1.24s no progress at version 758693 (avg 0.14s) [limit 15].\\nMax epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.56s no progress at version 2118371 (avg 0.56s) [limit 16].\\nTest Ok"\n}'
Trailing Log Lines:
networkchaos.chaos-mesh.org "4-gcp--as-southeast1-to-3-gcp--us-east4-netem" deleted
test CompositeNetworkTest ... ok
Test Statistics: 
two traffics test: inner traffic : committed: 11078.04 txn/s, submitted: 11082.79 txn/s, expired: 4.75 txn/s, latency: 3507.99 ms, (p50: 2900 ms, p70: 3000, p90: 3400 ms, p99: 28200 ms), latency samples: 4212095
two traffics test : committed: 99.97 txn/s, latency: 1307.63 ms, (p50: 1000 ms, p70: 1300, p90: 2300 ms, p99: 3600 ms), latency samples: 1660
Latency breakdown for phase 0: ["MempoolToBlockCreation: max: 2.718, avg: 2.468", "ConsensusProposalToOrdered: max: 0.196, avg: 0.190", "ConsensusOrderedToCommit: max: 0.242, avg: 0.229", "ConsensusProposalToCommit: max: 0.436, avg: 0.420"]
Max non-epoch-change gap was: 1 rounds at version 758693 (avg 0.00) [limit 4], 1.24s no progress at version 758693 (avg 0.14s) [limit 15].
Max epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.56s no progress at version 2118371 (avg 0.56s) [limit 16].
Test Ok

=== BEGIN JUNIT ===
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="forge" tests="1" failures="0" errors="0" uuid="f8151601-7383-4c41-a48f-56528d923ccf">
    <testsuite name="local" tests="1" disabled="0" errors="0" failures="0">
        <testcase name="CompositeNetworkTest(network:multi-region-network-emulation(two traffics test)) with ">
        </testcase>
    </testsuite>
</testsuites>
=== END JUNIT ===
[2025-05-23T15:51:57Z INFO  aptos_forge::backend::k8s::cluster_helper] Deleting namespace forge-e2e-pr-16638: Some(NamespaceStatus { conditions: None, phase: Some("Terminating") })
[2025-05-23T15:51:57Z INFO  aptos_forge::backend::k8s::cluster_helper] aptos-node resources for Forge removed in namespace: forge-e2e-pr-16638

test result: ok. 1 passed; 0 failed; 0 filtered out

Debugging output:
NAME                                   READY   STATUS      RESTARTS   AGE
aptos-node-0-fullnode-eforge38-0       1/1     Running     0          11m
aptos-node-0-validator-0               1/1     Running     0          11m
aptos-node-1-fullnode-eforge38-0       1/1     Running     0          11m
aptos-node-1-validator-0               1/1     Running     0          11m
aptos-node-2-fullnode-eforge38-0       1/1     Running     0          11m
aptos-node-2-validator-0               1/1     Running     0          11m
aptos-node-3-fullnode-eforge38-0       1/1     Running     0          11m
aptos-node-3-validator-0               1/1     Running     0          11m
aptos-node-4-fullnode-eforge38-0       1/1     Running     0          11m
aptos-node-4-validator-0               1/1     Running     0          11m
aptos-node-5-validator-0               1/1     Running     0          11m
aptos-node-6-validator-0               1/1     Running     0          11m
forge-testnet-deployer-v756t           0/1     Completed   0          11m
genesis-aptos-genesis-eforge38-tw87c   0/1     Completed   0          11m

```
* [Grafana dashboard](https://aptoslabs.grafana.net/d/overview/overview?orgId=1&refresh=10s&var-Datasource=VictoriaMetrics%20Global%20%28Non-mainnet%29&var-BigQuery=Google%20BigQuery&var-namespace=forge-e2e-pr-16638&var-metrics_source=All&var-chain_name=forge-0&from=1748014824000&to=1748015519000)
* [Humio Logs](https://cloud.us.humio.com/k8s/search?query=%24forgeLogs%28validator_instance%3D%2A%29+%7C%0A++++%22k8s.namespace%22+%3D+%22forge-e2e-pr-16638%22+%2F%2F+filters+on+namespace+which+contains+validator+logs%0A+++OR++%2F%2F+remove+either+side+of+the+OR+operator+to+only+display+validator+or+forge-runner+logs%0A++++%28%22k8s.namespace%22%3Ddefault+AND+%22k8s.labels.forge-namespace%22+%3D+%22forge-e2e-pr-16638%22%29+%2F%2F+filters+on+specific+forge-runner+pod+in+default+namespace%0A&widgetType=list-view&columns=%5B%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22%40timestamp%22%2C+%22format%22%3A+%22timestamp%22%2C+%22width%22%3A+180%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22level%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+54%7D%2C+%7B%22type%22%3A+%22link%22%2C+%22openInNewBrowserTab%22%3A+%22%2A%2A%2A%22%2C+%22style%22%3A+%22button%22%2C+%22hrefTemplate%22%3A+%22https%3A%2F%2Fgithub.com%2Faptos-labs%2Faptos-core%2Fpull%2F%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22textTemplate%22%3A+%22%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22header%22%3A+%22Forge+PR%22%2C+%22width%22%3A+79%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.namespace%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+104%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.pod_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+126%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.container_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+85%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22message%22%2C+%22format%22%3A+%22text%22%7D%5D&newestAtBottom=%2A%2A%2A&showOnlyFirstLine=false&live=false&start=1748014824000&end=1748015519000)
* [Axiom Logs](https://app.axiom.co/aptoslabs-hghf/explorer?initForm=%7B%22apl%22%3A%20%22%5Cn%20%20%20%20%20%20%20%20k8s%5Cn%20%20%20%20%20%20%20%20%7C%20where%20%5B%27k8s.cluster%27%5D%20contains%20%5C%22forge%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22calico-node%5C%22%20and%20%5B%27k8s.namespace%27%5D%20%21%3D%20%5C%22calico-apiserver%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22kube-proxy%5C%22%20and%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%28%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.namespace%27%5D%20%3D%3D%20%5C%22forge-e2e-pr-16638%5C%22%20//%20filters%20on%20namespace%20which%20contains%20validator%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20or%20//%20remove%20either%20side%20of%20the%20OR%20operator%20to%20only%20display%20validator%20or%20forge-runner%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.labels.forge-namespace%27%5D%20%3D%3D%20%5C%22forge-e2e-pr-16638%5C%22%20//%20filters%20on%20specific%20forge-runner%20pod%20in%20default%20namespace%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%29%5Cn%20%20%20%20%20%20%20%20%22%2C%20%22queryOptions%22%3A%20%7B%22startTime%22%3A%20%222025-05-23T15%3A40%3A24.074932%2B00%3A00%22%2C%20%22endTime%22%3A%20%222025-05-23T15%3A51%3A59.977846%2B00%3A00%22%7D%7D)
* [Validator CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=1748014824&until=1748015519&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-e2e-pr-16638&var-filters=pod%7C%3D~%7C.%2Avalidator.%2A)
* [Fullnode CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=1748014824&until=1748015519&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-e2e-pr-16638&var-filters=pod%7C%3D~%7C.%2Afullnode.%2A)
* [Test runner output](https://github.com/aptos-labs/aptos-core/actions/runs/15213747590)
* Test run is land-blocking
<!-- Sticky Pull Request Commentforge-e2e -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T15:53:37Z
body: ### :white_check_mark: Forge suite `framework_upgrade` success on `6ff41a58d52e09e11cf6d88c770b376f1108749d` ==> `424d541b3406a46d667725389acefb481efdefe7`
```
Compatibility test results for 6ff41a58d52e09e11cf6d88c770b376f1108749d ==> 424d541b3406a46d667725389acefb481efdefe7 (PR)
Upgrade the nodes to version: 424d541b3406a46d667725389acefb481efdefe7
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1531.35 txn/s, submitted: 1535.12 txn/s, failed submission: 3.76 txn/s, expired: 3.76 txn/s, latency: 1879.52 ms, (p50: 1800 ms, p70: 2100, p90: 2700 ms, p99: 4100 ms), latency samples: 138323
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1844.14 txn/s, submitted: 1848.33 txn/s, failed submission: 4.19 txn/s, expired: 4.19 txn/s, latency: 1621.13 ms, (p50: 1500 ms, p70: 1700, p90: 2200 ms, p99: 3200 ms), latency samples: 167301
5. check swarm health
Compatibility test for 6ff41a58d52e09e11cf6d88c770b376f1108749d ==> 424d541b3406a46d667725389acefb481efdefe7 passed
Upgrade the remaining nodes to version: 424d541b3406a46d667725389acefb481efdefe7
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1685.99 txn/s, submitted: 1693.86 txn/s, failed submission: 7.87 txn/s, expired: 7.87 txn/s, latency: 1650.56 ms, (p50: 1500 ms, p70: 1800, p90: 2100 ms, p99: 3000 ms), latency samples: 154260
Test Ok
```
* [Grafana dashboard](https://aptoslabs.grafana.net/d/overview/overview?orgId=1&refresh=10s&var-Datasource=VictoriaMetrics%20Global%20%28Non-mainnet%29&var-BigQuery=Google%20BigQuery&var-namespace=forge-framework-upgrade-pr-16638&var-metrics_source=All&var-chain_name=forge-0&from=1748014825000&to=1748015616000)
* [Humio Logs](https://cloud.us.humio.com/k8s/search?query=%24forgeLogs%28validator_instance%3D%2A%29+%7C%0A++++%22k8s.namespace%22+%3D+%22forge-framework-upgrade-pr-16638%22+%2F%2F+filters+on+namespace+which+contains+validator+logs%0A+++OR++%2F%2F+remove+either+side+of+the+OR+operator+to+only+display+validator+or+forge-runner+logs%0A++++%28%22k8s.namespace%22%3Ddefault+AND+%22k8s.labels.forge-namespace%22+%3D+%22forge-framework-upgrade-pr-16638%22%29+%2F%2F+filters+on+specific+forge-runner+pod+in+default+namespace%0A&widgetType=list-view&columns=%5B%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22%40timestamp%22%2C+%22format%22%3A+%22timestamp%22%2C+%22width%22%3A+180%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22level%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+54%7D%2C+%7B%22type%22%3A+%22link%22%2C+%22openInNewBrowserTab%22%3A+%22%2A%2A%2A%22%2C+%22style%22%3A+%22button%22%2C+%22hrefTemplate%22%3A+%22https%3A%2F%2Fgithub.com%2Faptos-labs%2Faptos-core%2Fpull%2F%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22textTemplate%22%3A+%22%7B%7Bfields%5B%5C%22github_pr%5C%22%5D%7D%7D%22%2C+%22header%22%3A+%22Forge+PR%22%2C+%22width%22%3A+79%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.namespace%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+104%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.pod_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+126%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22k8s.container_name%22%2C+%22format%22%3A+%22text%22%2C+%22width%22%3A+85%7D%2C+%7B%22type%22%3A+%22field%22%2C+%22fieldName%22%3A+%22message%22%2C+%22format%22%3A+%22text%22%7D%5D&newestAtBottom=%2A%2A%2A&showOnlyFirstLine=false&live=false&start=1748014825000&end=1748015616000)
* [Axiom Logs](https://app.axiom.co/aptoslabs-hghf/explorer?initForm=%7B%22apl%22%3A%20%22%5Cn%20%20%20%20%20%20%20%20k8s%5Cn%20%20%20%20%20%20%20%20%7C%20where%20%5B%27k8s.cluster%27%5D%20contains%20%5C%22forge%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22calico-node%5C%22%20and%20%5B%27k8s.namespace%27%5D%20%21%3D%20%5C%22calico-apiserver%5C%22%20and%20%5B%27k8s.container_name%27%5D%20%21%3D%20%5C%22kube-proxy%5C%22%20and%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%28%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.namespace%27%5D%20%3D%3D%20%5C%22forge-framework-upgrade-pr-16638%5C%22%20//%20filters%20on%20namespace%20which%20contains%20validator%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20or%20//%20remove%20either%20side%20of%20the%20OR%20operator%20to%20only%20display%20validator%20or%20forge-runner%20logs%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5B%27k8s.labels.forge-namespace%27%5D%20%3D%3D%20%5C%22forge-framework-upgrade-pr-16638%5C%22%20//%20filters%20on%20specific%20forge-runner%20pod%20in%20default%20namespace%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%29%5Cn%20%20%20%20%20%20%20%20%22%2C%20%22queryOptions%22%3A%20%7B%22startTime%22%3A%20%222025-05-23T15%3A40%3A25.629668%2B00%3A00%22%2C%20%22endTime%22%3A%20%222025-05-23T15%3A53%3A36.442135%2B00%3A00%22%7D%7D)
* [Validator CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=1748014825&until=1748015616&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-framework-upgrade-pr-16638&var-filters=pod%7C%3D~%7C.%2Avalidator.%2A)
* [Fullnode CPU Profile](https://grafana.aptoslabs.com/a/grafana-pyroscope-app/profiles-explorer?from=1748014825&until=1748015616&maxNodes=16384&explorationType=flame-graph&var-serviceName=ebpf%2Fforge&var-filters=namespace%7C%3D%7Cforge-framework-upgrade-pr-16638&var-filters=pod%7C%3D~%7C.%2Afullnode.%2A)
* [Test runner output](https://github.com/aptos-labs/aptos-core/actions/runs/15213747590)
* Test run is land-blocking
<!-- Sticky Pull Request Commentforge-framework-upgrade -->
author_association: CONTRIBUTOR