Repository: aptos-labs/aptos-core
File: third_party/move/move-vm/runtime/src/native_extensions.rs (Rust)

Code before:
// SPDX-License-Identifier: Apache-2.0

use better_any::{Tid, TidAble, TidExt};
use std::{any::TypeId, collections::HashMap};

/// Controls how VM session interacts with native extensions. For every session, we consider three
/// kinds of operations:
///   1. `start`: marks the start to some session S1.
///   2. `finish`: marks the end of some session S1. The changed made by S1 are saved, and some new
///      session S2 can now start.
///   3. `abort`: called when some session S2 aborts and needs to roll back to old state (session
///      S1), discarding all changes made by S2.
pub trait NativeExtensionSession {
    /// Called when session is aborted, to roll back the state to some previous session's state,
    /// just before a new session needs to start. If there is no session before
    fn abort(&mut self);

    /// Called on session finish, to save the data modified by the current extension. Called before
    /// a new session is about to start.
    fn finish(&mut self);

    /// Must be called on every session start.
    fn start(&mut self, txn_hash: &[u8; 32], script_hash: &[u8], session_counter: u8);
}

/// Any native extension should implement its version control. This way when a new extension gets
/// added there is a compile-time error when one tries to add it to the native context.
pub trait NativeExtension<'a>: NativeExtensionSession + Tid<'a> {}

impl<'a, T> NativeExtension<'a> for T where T: NativeExtensionSession + Tid<'a> {}

/// Marker for extensions that do not use session model and so there is no need to implement any
/// session operations.
pub trait UnreachableNativeExtensionSession {}

impl<T> NativeExtensionSession for T
where
    T: UnreachableNativeExtensionSession,
{
    fn abort(&mut self) {
        unreachable!("Irrelevant for extension")
    }

    fn finish(&mut self) {
        unreachable!("Irrelevant for extension")
    }

    fn start(&mut self, _txn_hash: &[u8; 32], _script_hash: &[u8], _session_counter: u8) {
        unreachable!("Irrelevant for extension")
    }
}

Review comment (suggestion):
I don't see 'version control' before mentioned in this module, and I find it confusing in the context of implementers of `NativeExtensionSession` / `SessionListener`. We don't need BlockSTM notions to describe this.

'Every native extension must explicitly implement the `SessionListner` API and decide whether it depends on session state'

Code after:
// SPDX-License-Identifier: Apache-2.0

use better_any::{Tid, TidAble, TidExt};
use std::{any::TypeId, collections::HashMap};

/// Controls how VM session interacts with native extensions. For every session, we consider three
/// kinds of operations:
///   1. `start`: marks the start to some session in state S1.
///   2. `finish`: marks the end of the current session state. The changes made by S are saved, and
///      the session is ready to transition to new state S2 (via calling `start`).
///   3. `abort`: called when the session aborts and needs to roll back to old state (e.g., if
///      currently at state S2, will move back to state S1), discarding all changes made by S2.
pub trait SessionListener {
    /// Must be called on every session start to transition to a new state.
    fn start(&mut self, session_hash: &[u8; 32], script_hash: &[u8], session_counter: u8);

    /// Called on session finish, to save the data modified by the current extension. Called before
    /// a transition to the new session via `start`.
    fn finish(&mut self);

    /// Called when session is aborted, to roll back the state to the previous session's state,
    /// just before a new session needs to start. If there is no session before, a no-op.
    fn abort(&mut self);
}

/// Any native extension should implement its interaction with the [SessionListener]. This way when
/// a new extension gets added there is a compile-time error when one tries to add it to the native
/// context.
pub trait NativeSessionListener<'a>: SessionListener + Tid<'a> {}

impl<'a, T> NativeSessionListener<'a> for T where T: SessionListener + Tid<'a> {}

/// Marker for extensions that do not use session model and so there is no need to implement any
/// session operations.
pub trait UnreachableSessionListener {}

impl<T> SessionListener for T
where
    T: UnreachableSessionListener,
{
    fn start(&mut self, _session_hash: &[u8; 32], _script_hash: &[u8], _session_counter: u8) {
        unreachable!("Irrelevant for extension")
    }

    fn finish(&mut self) {
        unreachable!("Irrelevant for extension")
    }

    fn abort(&mut self) {
        unreachable!("Irrelevant for extension")
    }