Skip to content

Commit

Permalink
WIP relative objects
Browse files Browse the repository at this point in the history
  • Loading branch information
graydon committed Jul 13, 2023
1 parent 42980d1 commit c5a4b96
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 41 deletions.
7 changes: 7 additions & 0 deletions soroban-env-common/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ pub trait EnvBase: Sized + Clone {
/// events are enabled. When running on host, logs directly; when running on
/// guest, redirects through log_from_linear_memory.
fn log_from_slice(&self, msg: &str, vals: &[Val]) -> Result<Void, Self::Error>;

fn make_relative(&self, val: Val) -> Val {
val
}
fn make_absolute(&self, val: Val) -> Val {
val
}
}

///////////////////////////////////////////////////////////////////////////////
Expand Down
24 changes: 12 additions & 12 deletions soroban-env-common/src/val.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
declare_tag_based_object_wrapper, declare_tag_based_wrapper, impl_rawval_wrapper_base,
impl_tryfroms_and_tryfromvals_delegating_to_rawvalconvertible, Compare, I32Val, SymbolSmall,
SymbolStr, U32Val,
impl_tryfroms_and_tryfromvals_delegating_to_rawvalconvertible, Compare, EnvBase, I32Val,
SymbolSmall, SymbolStr, U32Val,
};
use stellar_xdr::{ScError, ScValType};

Expand Down Expand Up @@ -374,51 +374,51 @@ impl_tryfroms_and_tryfromvals_delegating_to_rawvalconvertible!(Error);

#[cfg(feature = "wasmi")]
pub trait WasmiMarshal: Sized {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self>;
fn marshal_from_self(self) -> wasmi::Value;
fn try_marshal_from_value<E: EnvBase>(env: &E, v: wasmi::Value) -> Option<Self>;
fn marshal_from_self<E: EnvBase>(self, env: &E) -> wasmi::Value;
}

#[cfg(feature = "wasmi")]
impl WasmiMarshal for Val {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
fn try_marshal_from_value<E: EnvBase>(env: &E, v: wasmi::Value) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
Some(Val::from_payload(i as u64))
Some(env.make_absolute(Val::from_payload(i as u64)))
} else {
None
}
}

fn marshal_from_self(self) -> wasmi::Value {
wasmi::Value::I64(self.get_payload() as i64)
fn marshal_from_self<E: EnvBase>(self, env: &E) -> wasmi::Value {
wasmi::Value::I64(env.make_relative(self).get_payload() as i64)
}
}

#[cfg(feature = "wasmi")]
impl WasmiMarshal for u64 {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
fn try_marshal_from_value<E: EnvBase>(_env: &E, v: wasmi::Value) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
Some(i as u64)
} else {
None
}
}

fn marshal_from_self(self) -> wasmi::Value {
fn marshal_from_self<E: EnvBase>(self, _env: &E) -> wasmi::Value {
wasmi::Value::I64(self as i64)
}
}

#[cfg(feature = "wasmi")]
impl WasmiMarshal for i64 {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
fn try_marshal_from_value<E: EnvBase>(_env: &E, v: wasmi::Value) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
Some(i)
} else {
None
}
}

fn marshal_from_self(self) -> wasmi::Value {
fn marshal_from_self<E: EnvBase>(self, _env: &E) -> wasmi::Value {
wasmi::Value::I64(self)
}
}
Expand Down
18 changes: 12 additions & 6 deletions soroban-env-common/src/wrapper_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ macro_rules! impl_wrapper_wasmi_conversions {
// wasmi / VM argument support
#[cfg(feature = "wasmi")]
impl $crate::WasmiMarshal for $wrapper {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
fn try_marshal_from_value<E: $crate::EnvBase>(
env: &E,
v: wasmi::Value,
) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
let val = $crate::Val::from_payload(i as u64);
let val = env.make_absolute($crate::Val::from_payload(i as u64));
if <Self as $crate::val::ValConvert>::is_val_type(val) {
return Some(unsafe {
<Self as $crate::val::ValConvert>::unchecked_from_val(val)
Expand All @@ -96,8 +99,8 @@ macro_rules! impl_wrapper_wasmi_conversions {
None
}

fn marshal_from_self(self) -> wasmi::Value {
wasmi::Value::I64(self.as_val().get_payload() as i64)
fn marshal_from_self<E: $crate::EnvBase>(self, env: &E) -> wasmi::Value {
wasmi::Value::I64(env.make_relative(self.to_val()).get_payload() as i64)
}
}
};
Expand Down Expand Up @@ -189,7 +192,10 @@ macro_rules! declare_wasmi_marshal_for_enum {
($ENUM:ident) => {
#[cfg(feature = "wasmi")]
impl $crate::WasmiMarshal for $ENUM {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
fn try_marshal_from_value<E: $crate::EnvBase>(
_env: &E,
v: wasmi::Value,
) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
use num_traits::FromPrimitive;
$ENUM::from_i64(i)
Expand All @@ -198,7 +204,7 @@ macro_rules! declare_wasmi_marshal_for_enum {
}
}

fn marshal_from_self(self) -> wasmi::Value {
fn marshal_from_self<E: $crate::EnvBase>(self, _env: &E) -> wasmi::Value {
wasmi::Value::I64(self as i64)
}
}
Expand Down
62 changes: 57 additions & 5 deletions soroban-env-host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
err,
events::{diagnostic::DiagnosticLevel, Events, InternalEventsBuffer},
expiration_ledger_bumps::{ExpirationLedgerBumps, LedgerBump},
host_object::{HostMap, HostObject, HostObjectType, HostVec},
host_object::{
handle_to_index, index_to_handle, is_relative_object_handle, HostMap, HostObject,
HostObjectType, HostVec,
},
impl_bignum_host_fns_rhs_u32, impl_wrapping_obj_from_num, impl_wrapping_obj_to_num,
num::*,
storage::{InstanceStorageMap, Storage},
Expand Down Expand Up @@ -1098,6 +1101,55 @@ impl EnvBase for Host {
fn log_from_slice(&self, msg: &str, vals: &[Val]) -> Result<Void, HostError> {
self.log_diagnostics(msg, vals).map(|_| Void::from(()))
}

fn make_absolute(&self, val: Val) -> Val {
if let Ok(obj) = Object::try_from(val) {
let handle = obj.get_handle();
if is_relative_object_handle(handle) {
let index = handle_to_index(handle);
// FIXME: error handling
return self
.with_current_context_mut(|ctx| match ctx.relative_objects.get(index) {
Some(abs) => Ok(*abs),
None => Err(self.err(
ScErrorType::Context,
ScErrorCode::InvalidInput,
"unknown relative object reference",
&[val],
)),
})
.unwrap()
.to_val();
} else {
// FIXME: error handling
self.err(
ScErrorType::Context,
ScErrorCode::InvalidInput,
"make_absolute given an absolute reference",
&[val],
);
}
}
val
}

fn make_relative(&self, val: Val) -> Val {
if let Ok(obj) = Object::try_from(val) {
let handle = obj.get_handle();
if !is_relative_object_handle(handle) {
// FIXME: error handling
return self
.with_current_context_mut(|ctx| {
let index = ctx.relative_objects.len();
let handle = index_to_handle(self, index, true)?;
ctx.relative_objects.push(obj);
Ok(Object::from_handle_and_tag(handle, val.get_tag()).to_val())
})
.unwrap();
}
}
val
}
}

impl VmCallerEnv for Host {
Expand Down Expand Up @@ -1665,7 +1717,7 @@ impl VmCallerEnv for Host {
&vm,
vals_pos,
vals.as_mut_slice(),
|buf| Val::from_payload(u64::from_le_bytes(*buf)),
|buf| self.make_absolute(Val::from_payload(u64::from_le_bytes(*buf))),
)?;
for v in vals.iter() {
self.check_val_integrity(*v)?;
Expand Down Expand Up @@ -1726,7 +1778,7 @@ impl VmCallerEnv for Host {
&vm,
vals_pos.into(),
mapobj.map.as_slice(),
|pair| u64::to_le_bytes(pair.1.get_payload()),
|pair| u64::to_le_bytes(self.make_relative(pair.1).get_payload()),
)?;
Ok(())
})?;
Expand Down Expand Up @@ -1955,7 +2007,7 @@ impl VmCallerEnv for Host {
&vm,
pos,
vals.as_mut_slice(),
|buf| Val::from_payload(u64::from_le_bytes(*buf)),
|buf| self.make_absolute(Val::from_payload(u64::from_le_bytes(*buf))),
)?;
for v in vals.iter() {
self.check_val_integrity(*v)?;
Expand All @@ -1977,7 +2029,7 @@ impl VmCallerEnv for Host {
&vm,
vals_pos.into(),
vecobj.as_slice(),
|x| u64::to_le_bytes(x.get_payload()),
|x| u64::to_le_bytes(self.make_relative(*x).get_payload()),
)
})?;
Ok(Val::VOID)
Expand Down
19 changes: 18 additions & 1 deletion soroban-env-host/src/host/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
err,
storage::{InstanceStorageMap, StorageMap},
xdr::{ContractCostType, ContractExecutable, Hash, HostFunction, HostFunctionType, ScVal},
Error, Host, HostError, Symbol, SymbolStr, TryFromVal, TryIntoVal, Val,
Error, Host, HostError, Object, Symbol, SymbolStr, TryFromVal, TryIntoVal, Val,
};

#[cfg(any(test, feature = "testutils"))]
Expand Down Expand Up @@ -88,6 +88,7 @@ impl TestContractFrame {
pub(crate) struct Context {
pub(crate) frame: Frame,
prng: Option<Prng>,
pub(crate) relative_objects: Vec<Object>,
pub(crate) storage: Option<InstanceStorageMap>,
}

Expand Down Expand Up @@ -125,6 +126,7 @@ impl Host {
frame,
prng: None,
storage: None,
relative_objects: Vec::new(),
};
self.try_borrow_context_mut()?.push(ctx);
Ok(RollbackPoint {
Expand Down Expand Up @@ -246,6 +248,21 @@ impl Host {
}
}

pub(crate) fn with_current_context_mut_opt<F, U>(&self, f: F) -> Result<U, HostError>
where
F: FnOnce(Option<&mut Context>) -> Result<U, HostError>,
{
let Ok(mut context_guard) = self.0.context.try_borrow_mut() else {
return Err(self.err(ScErrorType::Context, ScErrorCode::InternalError, "context is already borrowed", &[]));
};
if let Some(context) = context_guard.last_mut() {
f(Some(context))
} else {
drop(context_guard);
f(None)
}
}

pub(crate) fn with_current_prng<F, U>(&self, f: F) -> Result<U, HostError>
where
F: FnOnce(&mut Prng) -> Result<U, HostError>,
Expand Down
74 changes: 66 additions & 8 deletions soroban-env-host/src/host_object.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#![allow(dead_code)]

use soroban_env_common::{
xdr::ContractCostType, Compare, DurationSmall, I128Small, I256Small, I64Small, SymbolSmall,
SymbolStr, Tag, TimepointSmall, U128Small, U256Small, U64Small,
xdr::{ContractCostType, ScErrorCode, ScErrorType},
Compare, DurationSmall, I128Small, I256Small, I64Small, SymbolSmall, SymbolStr, Tag,
TimepointSmall, U128Small, U256Small, U64Small,
};

use crate::{
Expand Down Expand Up @@ -169,6 +172,55 @@ declare_mem_host_object_type!(xdr::ScString, StringObject, String);
declare_mem_host_object_type!(xdr::ScSymbol, SymbolObject, Symbol);
declare_host_object_type!(xdr::ScAddress, AddressObject, Address);

// Objects come in two flavors: relative and absolute. Relative objects are the
// ones we pass to and from wasm code, and are looked up indirectly through a
// per-context table to find their absolute values. Absolute objects are the
// same in all contexts (and outside contexts, eg. in host objects themselves or
// while setting-up the host). Relative-to-absolute translation is done very
// close to the VM, when marshalling. Host code should never see relative object
// handles, and if you ever try to look one up in the host object table, it will
// fail.
//
// Also note: the relative/absolute object reference translation is _not_ done
// when running in native / local-testing mode, so you will not get identical
// object numbers in that case. Since there is no real isolation between
// contracts in that mode, there's no point bothering with the translation (and
// there's no really obvious place to perform it systematically, like in the
// wasm marshalling path).

pub fn is_relative_object_handle(handle: u32) -> bool {
handle & 1 == 0
}

pub fn is_relative_object(obj: Object) -> bool {
is_relative_object_handle(obj.get_handle())
}

pub fn is_relative_object_value(val: Val) -> bool {
if let Ok(obj) = Object::try_from(val) {
is_relative_object(obj)
} else {
false
}
}

pub fn handle_to_index(handle: u32) -> usize {
(handle as usize) >> 1
}

pub fn index_to_handle(host: &Host, index: usize, relative: bool) -> Result<u32, HostError> {
if let Ok(smaller) = u32::try_from(index) {
if let Some(shifted) = smaller.checked_shl(1) {
if relative {
return Ok(shifted | 0);
} else {
return Ok(shifted | 1);
}
}
}
Err(host.err_arith_overflow())
}

impl Host {
/// Moves a value of some type implementing [`HostObjectType`] into the host's
/// object array, returning a [`HostObj`] containing the new object's array
Expand All @@ -177,15 +229,12 @@ impl Host {
&self,
hot: HOT,
) -> Result<HOT::Wrapper, HostError> {
let prev_len = self.try_borrow_objects()?.len();
if prev_len > u32::MAX as usize {
return Err(self.err_arith_overflow());
}
let index = self.try_borrow_objects()?.len();
let handle = index_to_handle(self, index, false)?;
// charge for the new host object, which is just the amortized cost of a single
// `HostObject` allocation
metered_clone::charge_heap_alloc::<HostObject>(1, self.as_budget())?;
self.try_borrow_objects_mut()?.push(HOT::inject(hot));
let handle = prev_len as u32;
Ok(HOT::new_from_handle(handle))
}

Expand All @@ -203,7 +252,16 @@ impl Host {
let r = self.try_borrow_objects()?;
let obj: Object = obj.into();
let handle: u32 = obj.get_handle();
f(r.get(handle as usize))
if is_relative_object_handle(handle) {
Err(self.err(
ScErrorType::Context,
ScErrorCode::InternalError,
"looking up relative object",
&[obj.to_val()],
))
} else {
f(r.get(handle_to_index(handle)))
}
}

pub(crate) fn check_val_integrity(&self, val: Val) -> Result<(), HostError> {
Expand Down
Loading

0 comments on commit c5a4b96

Please sign in to comment.