mod applications;
pub mod committee;
mod execution;
mod graphql;
mod ownership;
pub mod pricing;
mod runtime;
pub mod system;
mod wasm;
pub use applications::{
    ApplicationId, ApplicationRegistryView, BytecodeLocation, UserApplicationDescription,
    UserApplicationId,
};
pub use execution::ExecutionStateView;
pub use ownership::ChainOwnership;
pub use system::{
    SystemExecutionError, SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery,
    SystemResponse,
};
#[cfg(all(
    any(test, feature = "test"),
    any(feature = "wasmer", feature = "wasmtime")
))]
pub use wasm::test as wasm_test;
#[cfg(any(feature = "wasmer", feature = "wasmtime"))]
pub use wasm::{WasmApplication, WasmExecutionError};
#[cfg(any(test, feature = "test"))]
pub use {applications::ApplicationRegistry, system::SystemExecutionState};
use async_graphql::SimpleObject;
use async_trait::async_trait;
use custom_debug_derive::Debug;
use dashmap::DashMap;
use derive_more::Display;
use linera_base::{
    abi::Abi,
    crypto::CryptoHash,
    data_types::{Amount, ArithmeticError, BlockHeight, Timestamp},
    hex_debug,
    identifiers::{BytecodeId, ChainId, ChannelName, Destination, MessageId, Owner, SessionId},
};
use linera_views::{batch::Batch, views::ViewError};
use serde::{Deserialize, Serialize};
use std::{io, path::Path, str::FromStr, sync::Arc};
use thiserror::Error;
pub type UserApplicationCode = Arc<dyn UserApplication + Send + Sync + 'static>;
#[derive(Error, Debug)]
pub enum ExecutionError {
    #[error(transparent)]
    ViewError(ViewError),
    #[error(transparent)]
    ArithmeticError(#[from] ArithmeticError),
    #[error(transparent)]
    SystemError(#[from] SystemExecutionError),
    #[error("User application reported an error: {0}")]
    UserError(String),
    #[cfg(any(feature = "wasmer", feature = "wasmtime"))]
    #[error(transparent)]
    WasmError(#[from] WasmExecutionError),
    #[error("A session is still opened at the end of a transaction")]
    SessionWasNotClosed,
    #[error("Invalid operation for this application")]
    InvalidOperation,
    #[error("Invalid message for this application")]
    InvalidMessage,
    #[error("Invalid query for this application")]
    InvalidQuery,
    #[error("Can't call another application during a query")]
    CallApplicationFromQuery,
    #[error("Queries can't change application state")]
    LockStateFromQuery,
    #[error("Session does not exist or was already closed")]
    InvalidSession,
    #[error("Attempted to call or forward an active session")]
    SessionIsInUse,
    #[error("Session is not accessible by this owner")]
    InvalidSessionOwner,
    #[error("Attempted to call an application while the state is locked")]
    ApplicationIsInUse,
    #[error("Attempted to get an entry that is not locked")]
    ApplicationStateNotLocked,
    #[error("Bytecode ID {0:?} is invalid")]
    InvalidBytecodeId(BytecodeId),
    #[error("Failed to load bytecode from storage {0:?}")]
    ApplicationBytecodeNotFound(Box<UserApplicationDescription>),
}
impl From<ViewError> for ExecutionError {
    fn from(error: ViewError) -> Self {
        match error {
            ViewError::TryLockError(_) => ExecutionError::ApplicationIsInUse,
            error => ExecutionError::ViewError(error),
        }
    }
}
#[async_trait]
pub trait UserApplication {
    async fn initialize(
        &self,
        context: &OperationContext,
        runtime: &dyn ContractRuntime,
        argument: &[u8],
    ) -> Result<RawExecutionResult<Vec<u8>>, ExecutionError>;
    async fn execute_operation(
        &self,
        context: &OperationContext,
        runtime: &dyn ContractRuntime,
        operation: &[u8],
    ) -> Result<RawExecutionResult<Vec<u8>>, ExecutionError>;
    async fn execute_message(
        &self,
        context: &MessageContext,
        runtime: &dyn ContractRuntime,
        message: &[u8],
    ) -> Result<RawExecutionResult<Vec<u8>>, ExecutionError>;
    async fn handle_application_call(
        &self,
        context: &CalleeContext,
        runtime: &dyn ContractRuntime,
        argument: &[u8],
        forwarded_sessions: Vec<SessionId>,
    ) -> Result<ApplicationCallResult, ExecutionError>;
    async fn handle_session_call(
        &self,
        context: &CalleeContext,
        runtime: &dyn ContractRuntime,
        session_state: &mut Vec<u8>,
        argument: &[u8],
        forwarded_sessions: Vec<SessionId>,
    ) -> Result<SessionCallResult, ExecutionError>;
    async fn query_application(
        &self,
        context: &QueryContext,
        runtime: &dyn ServiceRuntime,
        argument: &[u8],
    ) -> Result<Vec<u8>, ExecutionError>;
}
#[derive(Default)]
pub struct ApplicationCallResult {
    pub value: Vec<u8>,
    pub execution_result: RawExecutionResult<Vec<u8>>,
    pub create_sessions: Vec<Vec<u8>>,
}
#[derive(Default)]
pub struct SessionCallResult {
    pub inner: ApplicationCallResult,
    pub close_session: bool,
}
#[async_trait]
pub trait ExecutionRuntimeContext {
    fn chain_id(&self) -> ChainId;
    fn user_applications(&self) -> &Arc<DashMap<UserApplicationId, UserApplicationCode>>;
    async fn get_user_application(
        &self,
        description: &UserApplicationDescription,
    ) -> Result<UserApplicationCode, ExecutionError>;
}
#[derive(Clone, Copy, Debug)]
pub struct OperationContext {
    pub chain_id: ChainId,
    pub authenticated_signer: Option<Owner>,
    pub height: BlockHeight,
    pub index: u32,
    pub next_message_index: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct MessageContext {
    pub chain_id: ChainId,
    pub authenticated_signer: Option<Owner>,
    pub height: BlockHeight,
    pub certificate_hash: CryptoHash,
    pub message_id: MessageId,
}
#[derive(Clone, Copy, Debug)]
pub struct CalleeContext {
    pub chain_id: ChainId,
    pub authenticated_signer: Option<Owner>,
    pub authenticated_caller_id: Option<UserApplicationId>,
}
#[derive(Clone, Copy, Debug)]
pub struct QueryContext {
    pub chain_id: ChainId,
}
#[async_trait]
pub trait BaseRuntime: Send + Sync {
    fn chain_id(&self) -> ChainId;
    fn application_id(&self) -> UserApplicationId;
    fn application_parameters(&self) -> Vec<u8>;
    fn read_system_balance(&self) -> Amount;
    fn read_system_timestamp(&self) -> Timestamp;
    async fn try_read_my_state(&self) -> Result<Vec<u8>, ExecutionError>;
    async fn lock_view_user_state(&self) -> Result<(), ExecutionError>;
    async fn unlock_view_user_state(&self) -> Result<(), ExecutionError>;
    async fn read_key_bytes(&self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError>;
    async fn find_keys_by_prefix(
        &self,
        key_prefix: Vec<u8>,
    ) -> Result<Vec<Vec<u8>>, ExecutionError>;
    async fn find_key_values_by_prefix(
        &self,
        key_prefix: Vec<u8>,
    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
}
#[async_trait]
pub trait ServiceRuntime: BaseRuntime {
    async fn try_query_application(
        &self,
        queried_id: UserApplicationId,
        argument: &[u8],
    ) -> Result<Vec<u8>, ExecutionError>;
}
pub struct CallResult {
    pub value: Vec<u8>,
    pub sessions: Vec<SessionId>,
}
#[async_trait]
pub trait ContractRuntime: BaseRuntime {
    fn remaining_fuel(&self) -> u64;
    fn set_remaining_fuel(&self, remaining_fuel: u64);
    async fn try_read_and_lock_my_state(&self) -> Result<Vec<u8>, ExecutionError>;
    fn save_and_unlock_my_state(&self, state: Vec<u8>) -> Result<(), ExecutionError>;
    fn unlock_my_state(&self);
    async fn write_batch_and_unlock(&self, batch: Batch) -> Result<(), ExecutionError>;
    async fn try_call_application(
        &self,
        authenticated: bool,
        callee_id: UserApplicationId,
        argument: &[u8],
        forwarded_sessions: Vec<SessionId>,
    ) -> Result<CallResult, ExecutionError>;
    async fn try_call_session(
        &self,
        authenticated: bool,
        session_id: SessionId,
        argument: &[u8],
        forwarded_sessions: Vec<SessionId>,
    ) -> Result<CallResult, ExecutionError>;
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub enum Operation {
    System(SystemOperation),
    User {
        application_id: UserApplicationId,
        #[serde(with = "serde_bytes")]
        #[debug(with = "hex_debug")]
        bytes: Vec<u8>,
    },
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub enum Message {
    System(SystemMessage),
    User {
        application_id: UserApplicationId,
        #[serde(with = "serde_bytes")]
        #[debug(with = "hex_debug")]
        bytes: Vec<u8>,
    },
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub enum Query {
    System(SystemQuery),
    User {
        application_id: UserApplicationId,
        #[serde(with = "serde_bytes")]
        #[debug(with = "hex_debug")]
        bytes: Vec<u8>,
    },
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub enum Response {
    System(SystemResponse),
    User(
        #[serde(with = "serde_bytes")]
        #[debug(with = "hex_debug")]
        Vec<u8>,
    ),
}
#[derive(Debug)]
#[cfg_attr(any(test, feature = "test"), derive(Eq, PartialEq))]
pub struct RawExecutionResult<Message> {
    pub authenticated_signer: Option<Owner>,
    pub messages: Vec<(Destination, bool, Message)>,
    pub subscribe: Vec<(ChannelName, ChainId)>,
    pub unsubscribe: Vec<(ChannelName, ChainId)>,
}
#[derive(
    Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash, Serialize, Deserialize, SimpleObject,
)]
pub struct ChannelSubscription {
    pub chain_id: ChainId,
    pub name: ChannelName,
}
#[derive(Debug)]
#[cfg_attr(any(test, feature = "test"), derive(Eq, PartialEq))]
#[allow(clippy::large_enum_variant)]
pub enum ExecutionResult {
    System(RawExecutionResult<SystemMessage>),
    User(UserApplicationId, RawExecutionResult<Vec<u8>>),
}
impl ExecutionResult {
    pub fn application_id(&self) -> ApplicationId {
        match self {
            ExecutionResult::System(_) => ApplicationId::System,
            ExecutionResult::User(app_id, _) => ApplicationId::User(*app_id),
        }
    }
}
impl<Message> RawExecutionResult<Message> {
    pub fn with_authenticated_signer(mut self, authenticated_signer: Option<Owner>) -> Self {
        self.authenticated_signer = authenticated_signer;
        self
    }
}
impl<Message> Default for RawExecutionResult<Message> {
    fn default() -> Self {
        Self {
            authenticated_signer: None,
            messages: Vec::new(),
            subscribe: Vec::new(),
            unsubscribe: Vec::new(),
        }
    }
}
impl OperationContext {
    fn next_message_id(&self) -> MessageId {
        MessageId {
            chain_id: self.chain_id,
            height: self.height,
            index: self.next_message_index,
        }
    }
}
#[cfg(any(test, feature = "test"))]
#[derive(Clone)]
pub struct TestExecutionRuntimeContext {
    chain_id: ChainId,
    user_applications: Arc<DashMap<UserApplicationId, UserApplicationCode>>,
}
#[cfg(any(test, feature = "test"))]
impl TestExecutionRuntimeContext {
    fn new(chain_id: ChainId) -> Self {
        Self {
            chain_id,
            user_applications: Arc::default(),
        }
    }
}
#[cfg(any(test, feature = "test"))]
#[async_trait]
impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
    fn chain_id(&self) -> ChainId {
        self.chain_id
    }
    fn user_applications(&self) -> &Arc<DashMap<UserApplicationId, UserApplicationCode>> {
        &self.user_applications
    }
    async fn get_user_application(
        &self,
        description: &UserApplicationDescription,
    ) -> Result<UserApplicationCode, ExecutionError> {
        let application_id = description.into();
        Ok(self
            .user_applications()
            .get(&application_id)
            .ok_or_else(|| {
                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
            })?
            .clone())
    }
}
impl From<SystemOperation> for Operation {
    fn from(operation: SystemOperation) -> Self {
        Operation::System(operation)
    }
}
impl Operation {
    pub fn system(operation: SystemOperation) -> Self {
        Operation::System(operation)
    }
    pub fn user<A: Abi>(
        application_id: UserApplicationId<A>,
        operation: &A::Operation,
    ) -> Result<Self, bcs::Error> {
        let application_id = application_id.forget_abi();
        let bytes = bcs::to_bytes(&operation)?;
        Ok(Operation::User {
            application_id,
            bytes,
        })
    }
    pub fn application_id(&self) -> ApplicationId {
        match self {
            Self::System(_) => ApplicationId::System,
            Self::User { application_id, .. } => ApplicationId::User(*application_id),
        }
    }
}
impl From<SystemMessage> for Message {
    fn from(message: SystemMessage) -> Self {
        Message::System(message)
    }
}
impl Message {
    pub fn system(message: SystemMessage) -> Self {
        Message::System(message)
    }
    pub fn user<A: Abi>(
        application_id: UserApplicationId<A>,
        message: &A::Message,
    ) -> Result<Self, bcs::Error> {
        let application_id = application_id.forget_abi();
        let bytes = bcs::to_bytes(&message)?;
        Ok(Message::User {
            application_id,
            bytes,
        })
    }
    pub fn application_id(&self) -> ApplicationId {
        match self {
            Self::System(_) => ApplicationId::System,
            Self::User { application_id, .. } => ApplicationId::User(*application_id),
        }
    }
}
impl From<SystemQuery> for Query {
    fn from(query: SystemQuery) -> Self {
        Query::System(query)
    }
}
impl Query {
    pub fn system(query: SystemQuery) -> Self {
        Query::System(query)
    }
    pub fn user<A: Abi>(
        application_id: UserApplicationId<A>,
        query: &A::Query,
    ) -> Result<Self, serde_json::Error> {
        let application_id = application_id.forget_abi();
        let bytes = serde_json::to_vec(&query)?;
        Ok(Query::User {
            application_id,
            bytes,
        })
    }
    pub fn application_id(&self) -> ApplicationId {
        match self {
            Self::System(_) => ApplicationId::System,
            Self::User { application_id, .. } => ApplicationId::User(*application_id),
        }
    }
}
impl From<SystemResponse> for Response {
    fn from(response: SystemResponse) -> Self {
        Response::System(response)
    }
}
impl From<Vec<u8>> for Response {
    fn from(response: Vec<u8>) -> Self {
        Response::User(response)
    }
}
#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Bytecode {
    #[serde(with = "serde_bytes")]
    bytes: Vec<u8>,
}
impl Bytecode {
    #[cfg(any(feature = "wasmer", feature = "wasmtime"))]
    pub(crate) fn new(bytes: Vec<u8>) -> Self {
        Bytecode { bytes }
    }
    pub async fn load_from_file(path: impl AsRef<Path>) -> Result<Self, io::Error> {
        let bytes = tokio::fs::read(path).await?;
        Ok(Bytecode { bytes })
    }
}
impl AsRef<[u8]> for Bytecode {
    fn as_ref(&self) -> &[u8] {
        self.bytes.as_ref()
    }
}
impl std::fmt::Debug for Bytecode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        f.debug_tuple("Bytecode").finish()
    }
}
#[derive(Clone, Copy, Display)]
#[cfg_attr(any(feature = "wasmtime", feature = "wasmer"), derive(Debug, Default))]
pub enum WasmRuntime {
    #[cfg(feature = "wasmer")]
    #[default]
    #[display(fmt = "wasmer")]
    Wasmer,
    #[cfg(feature = "wasmtime")]
    #[cfg_attr(not(feature = "wasmer"), default)]
    #[display(fmt = "wasmtime")]
    Wasmtime,
    #[cfg(feature = "wasmer")]
    WasmerWithSanitizer,
    #[cfg(feature = "wasmtime")]
    WasmtimeWithSanitizer,
}
pub trait WithWasmDefault {
    fn with_wasm_default(self) -> Self;
}
impl WasmRuntime {
    #[cfg(any(feature = "wasmer", feature = "wasmtime"))]
    pub fn default_with_sanitizer() -> Self {
        #[cfg(feature = "wasmer")]
        {
            WasmRuntime::WasmerWithSanitizer
        }
        #[cfg(not(feature = "wasmer"))]
        {
            WasmRuntime::WasmtimeWithSanitizer
        }
    }
    pub fn needs_sanitizer(self) -> bool {
        match self {
            #[cfg(feature = "wasmer")]
            WasmRuntime::WasmerWithSanitizer => true,
            #[cfg(feature = "wasmtime")]
            WasmRuntime::WasmtimeWithSanitizer => true,
            #[cfg(any(feature = "wasmtime", feature = "wasmer"))]
            _ => false,
        }
    }
}
impl WithWasmDefault for Option<WasmRuntime> {
    #[cfg(any(feature = "wasmer", feature = "wasmtime"))]
    fn with_wasm_default(self) -> Self {
        Some(self.unwrap_or_default())
    }
    #[cfg(not(any(feature = "wasmer", feature = "wasmtime")))]
    fn with_wasm_default(self) -> Self {
        None
    }
}
impl FromStr for WasmRuntime {
    type Err = InvalidWasmRuntime;
    fn from_str(string: &str) -> Result<Self, Self::Err> {
        match string {
            #[cfg(feature = "wasmer")]
            "wasmer" => Ok(WasmRuntime::Wasmer),
            #[cfg(feature = "wasmtime")]
            "wasmtime" => Ok(WasmRuntime::Wasmtime),
            unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
        }
    }
}
#[derive(Clone, Debug, Error)]
#[error("{0:?} is not a valid WebAssembly runtime")]
pub struct InvalidWasmRuntime(String);