use std::{any::Any, collections::HashMap, marker::PhantomData};
use linera_base::{
crypto::CryptoHash,
data_types::{Amount, ApplicationPermissions, BlockHeight, SendMessageRequest, Timestamp},
http,
identifiers::{
Account, AccountOwner, ApplicationId, ChainId, ChannelName, MessageId, Owner, StreamName,
},
ownership::{ChainOwnership, ChangeApplicationPermissionsError, CloseChainError},
};
use linera_views::batch::{Batch, WriteOperation};
use linera_witty::{wit_export, Instance, RuntimeError};
use tracing::log;
use super::WasmExecutionError;
use crate::{BaseRuntime, BytecodeId, ContractRuntime, ExecutionError, 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) -> Result<u32, RuntimeError>
where
Promise: Send + Sync + 'static,
{
let id = self.promise_counter;
self.active_promises.insert(id, Box::new(promise));
self.promise_counter += 1;
Ok(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 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 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: CryptoHash) -> 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: CryptoHash) -> 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> {
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()))?;
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()))?;
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()))?;
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()))?;
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()))?;
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()))?;
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<Owner>, RuntimeError> {
caller
.user_data_mut()
.runtime
.authenticated_signer()
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn get_message_id(caller: &mut Caller) -> Result<Option<MessageId>, RuntimeError> {
caller
.user_data_mut()
.runtime
.message_id()
.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 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 subscribe(
caller: &mut Caller,
chain: ChainId,
channel: ChannelName,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.subscribe(chain, channel)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn unsubscribe(
caller: &mut Caller,
chain: ChainId,
channel: ChannelName,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.unsubscribe(chain, channel)
.map_err(|error| RuntimeError::Custom(error.into()))
}
fn transfer(
caller: &mut Caller,
source: Option<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<(MessageId, 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_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,
bytecode_id: BytecodeId,
parameters: Vec<u8>,
argument: Vec<u8>,
required_application_ids: Vec<ApplicationId>,
) -> Result<ApplicationId, RuntimeError> {
caller
.user_data_mut()
.runtime
.create_application(bytecode_id, parameters, argument, required_application_ids)
.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,
key: Vec<u8>,
value: Vec<u8>,
) -> Result<(), RuntimeError> {
caller
.user_data_mut()
.runtime
.emit(name, key, value)
.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)
.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 fetch_url(caller: &mut Caller, url: String) -> Result<Vec<u8>, RuntimeError> {
caller
.user_data_mut()
.runtime
.fetch_url(&url)
.map_err(|error| RuntimeError::Custom(error.into()))
}
}