use std::{any::Any, collections::HashMap, marker::PhantomData};
use linera_base::{
data_types::{
Amount, ApplicationDescription, ApplicationPermissions, BlockHeight, Bytecode,
SendMessageRequest, Timestamp,
},
http,
identifiers::{Account, AccountOwner, ApplicationId, ChainId, StreamName},
ownership::{
ChainOwnership, ChangeApplicationPermissionsError, ChangeOwnershipError, CloseChainError,
},
vm::VmRuntime,
};
use linera_views::batch::{Batch, WriteOperation};
use linera_witty::{wit_export, Instance, RuntimeError};
use tracing::log;
use super::WasmExecutionError;
use crate::{BaseRuntime, ContractRuntime, DataBlobHash, ExecutionError, ModuleId, ServiceRuntime};
pub struct RuntimeApiData<Runtime> {
runtime: Runtime,
active_promises: HashMap<u32, Box<dyn Any + Send + Sync>>,
promise_counter: u32,
}
impl<Runtime> RuntimeApiData<Runtime> {
pub fn new(runtime: Runtime) -> Self {
RuntimeApiData {
runtime,
active_promises: HashMap::new(),
promise_counter: 0,
}
}
pub fn runtime_mut(&mut self) -> &mut Runtime {
&mut self.runtime
}
fn register_promise<Promise>(&mut self, promise: Promise) -> u32
where
Promise: Send + Sync + 'static,
{
let id = self.promise_counter;
self.active_promises.insert(id, Box::new(promise));
self.promise_counter += 1;
id
}
fn take_promise<Promise>(&mut self, promise_id: u32) -> Result<Promise, RuntimeError>
where
Promise: Send + Sync + 'static,
{
let type_erased_promise = self
.active_promises
.remove(&promise_id)
.ok_or_else(|| RuntimeError::Custom(WasmExecutionError::UnknownPromise.into()))?;
type_erased_promise
.downcast()
.map(|boxed_promise| *boxed_promise)
.map_err(|_| RuntimeError::Custom(WasmExecutionError::IncorrectPromise.into()))
}
}
#[derive(Default)]
pub struct BaseRuntimeApi<Caller>(PhantomData<Caller>);
#[wit_export(package = "linera:app")]
impl<Caller, Runtime> BaseRuntimeApi<Caller>
where
Caller: Instance<UserData = RuntimeApiData<Runtime>>,
Runtime: BaseRuntime + 'static,
{
fn get_chain_id(caller: &mut Caller) -> Result<ChainId, RuntimeError> {
caller
.user_data_mut()
.runtime
.chain_id()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn get_block_height(caller: &mut Caller) -> Result<BlockHeight, RuntimeError> {
caller
.user_data_mut()
.runtime
.block_height()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn get_application_id(caller: &mut Caller) -> Result<ApplicationId, RuntimeError> {
caller
.user_data_mut()
.runtime
.application_id()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn get_application_creator_chain_id(caller: &mut Caller) -> Result<ChainId, RuntimeError> {
caller
.user_data_mut()
.runtime
.application_creator_chain_id()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_application_description(
caller: &mut Caller,
application_id: ApplicationId,
) -> Result<ApplicationDescription, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_application_description(application_id)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn application_parameters(caller: &mut Caller) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.application_parameters()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn get_chain_ownership(caller: &mut Caller) -> Result<ChainOwnership, RuntimeError> {
caller
.user_data_mut()
.runtime
.chain_ownership()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn get_application_permissions(
caller: &mut Caller,
) -> Result<ApplicationPermissions, RuntimeError> {
caller
.user_data_mut()
.runtime
.application_permissions()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_system_timestamp(caller: &mut Caller) -> Result<Timestamp, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_system_timestamp()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_chain_balance(caller: &mut Caller) -> Result<Amount, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_chain_balance()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_owner_balance(
caller: &mut Caller,
owner: AccountOwner,
) -> Result<Amount, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_owner_balance(owner)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_owner_balances(
caller: &mut Caller,
) -> Result<Vec<(AccountOwner, Amount)>, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_owner_balances()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_balance_owners(caller: &mut Caller) -> Result<Vec<AccountOwner>, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_balance_owners()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn perform_http_request(
caller: &mut Caller,
request: http::Request,
) -> Result<http::Response, RuntimeError> {
caller
.user_data_mut()
.runtime
.perform_http_request(request)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn assert_before(caller: &mut Caller, timestamp: Timestamp) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.assert_before(timestamp)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_data_blob(caller: &mut Caller, hash: DataBlobHash) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_data_blob(hash)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn assert_data_blob_exists(
caller: &mut Caller,
hash: DataBlobHash,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.assert_data_blob_exists(hash)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn log(caller: &mut Caller, message: String, level: log::Level) -> Result<(), RuntimeError> {
let allowed = caller
.user_data_mut()
.runtime
.allow_application_logs()
.map_err(|error| RuntimeError::Custom(error.into()))?;
if !allowed {
return Ok(());
}
#[cfg(web)]
{
caller
.user_data_mut()
.runtime
.send_log(message.clone(), level);
}
match level {
log::Level::Trace => tracing::trace!("{message}"),
log::Level::Debug => tracing::debug!("{message}"),
log::Level::Info => tracing::info!("{message}"),
log::Level::Warn => tracing::warn!("{message}"),
log::Level::Error => tracing::error!("{message}"),
}
Ok(())
}
fn contains_key_new(caller: &mut Caller, key: Vec<u8>) -> Result<u32, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data
.runtime
.contains_key_new(key)
.map_err(|error| RuntimeError::Custom(error.into()))?;
Ok(data.register_promise(promise))
}
fn contains_key_wait(caller: &mut Caller, promise_id: u32) -> Result<bool, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data.take_promise(promise_id)?;
data.runtime
.contains_key_wait(&promise)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn contains_keys_new(caller: &mut Caller, keys: Vec<Vec<u8>>) -> Result<u32, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data
.runtime
.contains_keys_new(keys)
.map_err(|error| RuntimeError::Custom(error.into()))?;
Ok(data.register_promise(promise))
}
fn contains_keys_wait(caller: &mut Caller, promise_id: u32) -> Result<Vec<bool>, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data.take_promise(promise_id)?;
data.runtime
.contains_keys_wait(&promise)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_multi_values_bytes_new(
caller: &mut Caller,
keys: Vec<Vec<u8>>,
) -> Result<u32, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data
.runtime
.read_multi_values_bytes_new(keys)
.map_err(|error| RuntimeError::Custom(error.into()))?;
Ok(data.register_promise(promise))
}
fn read_multi_values_bytes_wait(
caller: &mut Caller,
promise_id: u32,
) -> Result<Vec<Option<Vec<u8>>>, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data.take_promise(promise_id)?;
data.runtime
.read_multi_values_bytes_wait(&promise)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_value_bytes_new(caller: &mut Caller, key: Vec<u8>) -> Result<u32, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data
.runtime
.read_value_bytes_new(key)
.map_err(|error| RuntimeError::Custom(error.into()))?;
Ok(data.register_promise(promise))
}
fn read_value_bytes_wait(
caller: &mut Caller,
promise_id: u32,
) -> Result<Option<Vec<u8>>, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data.take_promise(promise_id)?;
data.runtime
.read_value_bytes_wait(&promise)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn find_keys_new(caller: &mut Caller, key_prefix: Vec<u8>) -> Result<u32, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data
.runtime
.find_keys_by_prefix_new(key_prefix)
.map_err(|error| RuntimeError::Custom(error.into()))?;
Ok(data.register_promise(promise))
}
fn find_keys_wait(caller: &mut Caller, promise_id: u32) -> Result<Vec<Vec<u8>>, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data.take_promise(promise_id)?;
data.runtime
.find_keys_by_prefix_wait(&promise)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn find_key_values_new(caller: &mut Caller, key_prefix: Vec<u8>) -> Result<u32, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data
.runtime
.find_key_values_by_prefix_new(key_prefix)
.map_err(|error| RuntimeError::Custom(error.into()))?;
Ok(data.register_promise(promise))
}
#[expect(clippy::type_complexity)]
fn find_key_values_wait(
caller: &mut Caller,
promise_id: u32,
) -> Result<Vec<(Vec<u8>, Vec<u8>)>, RuntimeError> {
let mut data = caller.user_data_mut();
let promise = data.take_promise(promise_id)?;
data.runtime
.find_key_values_by_prefix_wait(&promise)
.map_err(|error| RuntimeError::Custom(error.into()))
}
}
#[derive(Default)]
pub struct ContractRuntimeApi<Caller>(PhantomData<Caller>);
#[wit_export(package = "linera:app")]
impl<Caller, Runtime> ContractRuntimeApi<Caller>
where
Caller: Instance<UserData = RuntimeApiData<Runtime>>,
Runtime: ContractRuntime + 'static,
{
fn authenticated_signer(caller: &mut Caller) -> Result<Option<AccountOwner>, RuntimeError> {
caller
.user_data_mut()
.runtime
.authenticated_signer()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn message_is_bouncing(caller: &mut Caller) -> Result<Option<bool>, RuntimeError> {
caller
.user_data_mut()
.runtime
.message_is_bouncing()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn message_origin_chain_id(caller: &mut Caller) -> Result<Option<ChainId>, RuntimeError> {
caller
.user_data_mut()
.runtime
.message_origin_chain_id()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn authenticated_caller_id(caller: &mut Caller) -> Result<Option<ApplicationId>, RuntimeError> {
caller
.user_data_mut()
.runtime
.authenticated_caller_id()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn send_message(
caller: &mut Caller,
message: SendMessageRequest<Vec<u8>>,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.send_message(message)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn transfer(
caller: &mut Caller,
source: AccountOwner,
destination: Account,
amount: Amount,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.transfer(source, destination, amount)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn claim(
caller: &mut Caller,
source: Account,
destination: Account,
amount: Amount,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.claim(source, destination, amount)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn open_chain(
caller: &mut Caller,
chain_ownership: ChainOwnership,
application_permissions: ApplicationPermissions,
balance: Amount,
) -> Result<ChainId, RuntimeError> {
caller
.user_data_mut()
.runtime
.open_chain(chain_ownership, application_permissions, balance)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn close_chain(caller: &mut Caller) -> Result<Result<(), CloseChainError>, RuntimeError> {
match caller.user_data_mut().runtime.close_chain() {
Ok(()) => Ok(Ok(())),
Err(ExecutionError::UnauthorizedApplication(_)) => {
Ok(Err(CloseChainError::NotPermitted))
}
Err(error) => Err(RuntimeError::Custom(error.into())),
}
}
fn change_ownership(
caller: &mut Caller,
ownership: ChainOwnership,
) -> Result<Result<(), ChangeOwnershipError>, RuntimeError> {
match caller.user_data_mut().runtime.change_ownership(ownership) {
Ok(()) => Ok(Ok(())),
Err(ExecutionError::UnauthorizedApplication(_)) => {
Ok(Err(ChangeOwnershipError::NotPermitted))
}
Err(error) => Err(RuntimeError::Custom(error.into())),
}
}
fn change_application_permissions(
caller: &mut Caller,
application_permissions: ApplicationPermissions,
) -> Result<Result<(), ChangeApplicationPermissionsError>, RuntimeError> {
match caller
.user_data_mut()
.runtime
.change_application_permissions(application_permissions)
{
Ok(()) => Ok(Ok(())),
Err(ExecutionError::UnauthorizedApplication(_)) => {
Ok(Err(ChangeApplicationPermissionsError::NotPermitted))
}
Err(error) => Err(RuntimeError::Custom(error.into())),
}
}
fn create_application(
caller: &mut Caller,
module_id: ModuleId,
parameters: Vec<u8>,
argument: Vec<u8>,
required_application_ids: Vec<ApplicationId>,
) -> Result<ApplicationId, RuntimeError> {
caller
.user_data_mut()
.runtime
.create_application(module_id, parameters, argument, required_application_ids)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn create_data_blob(caller: &mut Caller, bytes: Vec<u8>) -> Result<DataBlobHash, RuntimeError> {
caller
.user_data_mut()
.runtime
.create_data_blob(bytes)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn publish_module(
caller: &mut Caller,
contract: Bytecode,
service: Bytecode,
vm_runtime: VmRuntime,
) -> Result<ModuleId, RuntimeError> {
caller
.user_data_mut()
.runtime
.publish_module(contract, service, vm_runtime)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn try_call_application(
caller: &mut Caller,
authenticated: bool,
callee_id: ApplicationId,
argument: Vec<u8>,
) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.try_call_application(authenticated, callee_id, argument)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn emit(caller: &mut Caller, name: StreamName, value: Vec<u8>) -> Result<u32, RuntimeError> {
caller
.user_data_mut()
.runtime
.emit(name, value)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn read_event(
caller: &mut Caller,
chain_id: ChainId,
name: StreamName,
index: u32,
) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.read_event(chain_id, name, index)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn subscribe_to_events(
caller: &mut Caller,
chain_id: ChainId,
application_id: ApplicationId,
name: StreamName,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.subscribe_to_events(chain_id, application_id, name)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn unsubscribe_from_events(
caller: &mut Caller,
chain_id: ChainId,
application_id: ApplicationId,
name: StreamName,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.unsubscribe_from_events(chain_id, application_id, name)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn query_service(
caller: &mut Caller,
application_id: ApplicationId,
query: Vec<u8>,
) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.query_service(application_id, query)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn consume_fuel(caller: &mut Caller, fuel: u64) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime_mut()
.consume_fuel(fuel, VmRuntime::Wasm)
.map_err(|e| RuntimeError::Custom(e.into()))
}
fn validation_round(caller: &mut Caller) -> Result<Option<u32>, RuntimeError> {
caller
.user_data_mut()
.runtime_mut()
.validation_round()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn write_batch(
caller: &mut Caller,
operations: Vec<WriteOperation>,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime_mut()
.write_batch(Batch { operations })
.map_err(|error| RuntimeError::Custom(error.into()))
}
}
#[derive(Default)]
pub struct ServiceRuntimeApi<Caller>(PhantomData<Caller>);
#[wit_export(package = "linera:app")]
impl<Caller, Runtime> ServiceRuntimeApi<Caller>
where
Caller: Instance<UserData = RuntimeApiData<Runtime>>,
Runtime: ServiceRuntime + 'static,
{
fn schedule_operation(caller: &mut Caller, operation: Vec<u8>) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.schedule_operation(operation)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn try_query_application(
caller: &mut Caller,
application: ApplicationId,
argument: Vec<u8>,
) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.try_query_application(application, argument)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn check_execution_time(caller: &mut Caller, _fuel_consumed: u64) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime_mut()
.check_execution_time()
.map_err(|error| RuntimeError::Custom(error.into()))
}
}