Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wasmtime/winch sketch #1507

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
639 changes: 603 additions & 36 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ soroban-builtin-sdk-macros = { version = "=22.1.3", path = "soroban-builtin-sdk-
# NB: this must match the wasmparser version wasmi is using
wasmparser = "=0.116.1"

[workspace.dependencies.wasmtime]
version = "28.0"
default-features = false
features = ["runtime", "winch"]
git = "https://github.com/bytecodealliance/wasmtime"
rev = "b5627a86a7740ffc732f4c22b9f0b2c66252638b"

# NB: When updating, also update the version in rs-soroban-env dev-dependencies
[workspace.dependencies.stellar-xdr]
version = "=22.1.0"
Expand Down
2 changes: 2 additions & 0 deletions soroban-env-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ethnum = "1.5.0"
arbitrary = { version = "1.3.2", features = ["derive"], optional = true }
num-traits = {version = "0.2.17", default-features = false}
num-derive = "0.4.1"
wasmtime = { workspace = true, optional = true}

[target.'cfg(not(target_family = "wasm"))'.dependencies]
tracy-client = { version = "0.17.0", features = ["enable", "timer-fallback"], default-features = false, optional = true }
Expand All @@ -36,6 +37,7 @@ num-traits = "0.2.17"
std = ["stellar-xdr/std", "stellar-xdr/base64"]
serde = ["dep:serde", "stellar-xdr/serde"]
wasmi = ["dep:wasmi", "dep:wasmparser"]
wasmtime = ["dep:wasmtime"]
testutils = ["dep:arbitrary", "stellar-xdr/arbitrary"]
next = ["stellar-xdr/next", "soroban-env-macros/next"]
tracy = ["dep:tracy-client"]
Expand Down
43 changes: 43 additions & 0 deletions soroban-env-common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,49 @@ impl From<wasmparser::BinaryReaderError> for Error {
}
}

#[cfg(feature = "wasmtime")]
impl From<wasmtime::Trap> for Error {
#[allow(clippy::wildcard_in_or_patterns)]
fn from(trap: wasmtime::Trap) -> Self {
let ec = match trap {
wasmtime::Trap::UnreachableCodeReached => ScErrorCode::InvalidAction,

wasmtime::Trap::MemoryOutOfBounds | wasmtime::Trap::TableOutOfBounds => {
ScErrorCode::IndexBounds
}

wasmtime::Trap::IndirectCallToNull => ScErrorCode::MissingValue,

wasmtime::Trap::IntegerDivisionByZero
| wasmtime::Trap::IntegerOverflow
| wasmtime::Trap::BadConversionToInteger => ScErrorCode::ArithDomain,

wasmtime::Trap::BadSignature => ScErrorCode::UnexpectedType,

wasmtime::Trap::StackOverflow
| wasmtime::Trap::Interrupt
| wasmtime::Trap::OutOfFuel => {
return Error::from_type_and_code(ScErrorType::Budget, ScErrorCode::ExceededLimit)
}

wasmtime::Trap::HeapMisaligned
| wasmtime::Trap::AlwaysTrapAdapter
| wasmtime::Trap::AtomicWaitNonSharedMemory
| wasmtime::Trap::NullReference
| wasmtime::Trap::CannotEnterComponent
| _ => ScErrorCode::InvalidAction,
};
Error::from_type_and_code(ScErrorType::WasmVm, ec)
}
}

#[cfg(feature = "wasmtime")]
impl From<wasmtime::MemoryAccessError> for Error {
fn from(_: wasmtime::MemoryAccessError) -> Self {
Error::from_type_and_code(ScErrorType::WasmVm, ScErrorCode::IndexBounds)
}
}

impl Error {
// NB: we don't provide a "get_type" to avoid casting a bad bit-pattern into
// an ScErrorType. Instead we provide an "is_type" to check any specific
Expand Down
2 changes: 2 additions & 0 deletions soroban-env-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ pub use val::{ConversionError, Tag, Val};

#[cfg(feature = "wasmi")]
pub use val::WasmiMarshal;
#[cfg(feature = "wasmtime")]
pub use val::WasmtimeMarshal;
pub use val::{AddressObject, MapObject, VecObject};
pub use val::{Bool, Void};

Expand Down
56 changes: 56 additions & 0 deletions soroban-env-common/src/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,62 @@ impl WasmiMarshal for i64 {
}
}

#[cfg(feature = "wasmtime")]
pub trait WasmtimeMarshal: Sized {
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self>;
fn marshal_wasmtime_from_self(self) -> wasmtime::Val;
}

#[cfg(feature = "wasmtime")]
impl WasmtimeMarshal for Val {
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self> {
if let wasmtime::Val::I64(i) = v {
let v = Val::from_payload(i as u64);
if v.is_good() {
Some(v)
} else {
None
}
} else {
None
}
}

fn marshal_wasmtime_from_self(self) -> wasmtime::Val {
wasmtime::Val::I64(self.get_payload() as i64)
}
}

#[cfg(feature = "wasmtime")]
impl WasmtimeMarshal for u64 {
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self> {
if let wasmtime::Val::I64(i) = v {
Some(i as u64)
} else {
None
}
}

fn marshal_wasmtime_from_self(self) -> wasmtime::Val {
wasmtime::Val::I64(self as i64)
}
}

#[cfg(feature = "wasmtime")]
impl WasmtimeMarshal for i64 {
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self> {
if let wasmtime::Val::I64(i) = v {
Some(i)
} else {
None
}
}

fn marshal_wasmtime_from_self(self) -> wasmtime::Val {
wasmtime::Val::I64(self)
}
}

// Manually implement all the residual pieces: ValConverts
// and Froms.

Expand Down
53 changes: 41 additions & 12 deletions soroban-env-common/src/vmcaller_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,67 @@ use core::marker::PhantomData;
/// allows code to import and use `Env` directly (such as the native
/// contract) to call host methods without having to write `VmCaller::none()`
/// everywhere.
#[cfg(feature = "wasmi")]
pub struct VmCaller<'a, T>(pub Option<wasmi::Caller<'a, T>>);
#[cfg(feature = "wasmi")]

#[cfg(any(feature = "wasmi", feature = "wasmtime"))]
pub enum VmCaller<'a, T> {
#[cfg(feature = "wasmi")]
WasmiCaller(wasmi::Caller<'a, T>),
#[cfg(feature = "wasmtime")]
WasmtimeCaller(wasmtime::Caller<'a, T>),
NoCaller,
}
#[cfg(any(feature = "wasmi", feature = "wasmtime"))]
impl<'a, T> VmCaller<'a, T> {
pub fn none() -> Self {
VmCaller(None)
VmCaller::NoCaller
}
#[cfg(feature = "wasmi")]
pub fn try_ref(&self) -> Result<&wasmi::Caller<'a, T>, Error> {
match &self.0 {
Some(caller) => Ok(caller),
None => Err(Error::from_type_and_code(
match self {
VmCaller::WasmiCaller(caller) => Ok(caller),
_ => Err(Error::from_type_and_code(
ScErrorType::Context,
ScErrorCode::InternalError,
)),
}
}
#[cfg(feature = "wasmi")]
pub fn try_mut(&mut self) -> Result<&mut wasmi::Caller<'a, T>, Error> {
match &mut self.0 {
Some(caller) => Ok(caller),
None => Err(Error::from_type_and_code(
match self {
VmCaller::WasmiCaller(caller) => Ok(caller),
_ => Err(Error::from_type_and_code(
ScErrorType::Context,
ScErrorCode::InternalError,
)),
}
}
#[cfg(feature = "wasmtime")]
pub fn try_ref_wasmtime(&self) -> Result<&wasmtime::Caller<'a, T>, Error> {
match self {
VmCaller::WasmtimeCaller(caller) => Ok(caller),
_ => Err(Error::from_type_and_code(
ScErrorType::Context,
ScErrorCode::InternalError,
)),
}
}
#[cfg(feature = "wasmtime")]
pub fn try_mut_wasmtime(&mut self) -> Result<&mut wasmtime::Caller<'a, T>, Error> {
match self {
VmCaller::WasmtimeCaller(caller) => Ok(caller),
_ => Err(Error::from_type_and_code(
ScErrorType::Context,
ScErrorCode::InternalError,
)),
}
}
}

#[cfg(not(feature = "wasmi"))]
#[cfg(not(any(feature = "wasmi", feature = "wasmtime")))]
pub struct VmCaller<'a, T> {
_nothing: PhantomData<&'a T>,
}
#[cfg(not(feature = "wasmi"))]
#[cfg(not(any(feature = "wasmi", feature = "wasmtime")))]
impl<'a, T> VmCaller<'a, T> {
pub fn none() -> Self {
VmCaller {
Expand Down
8 changes: 6 additions & 2 deletions soroban-env-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ exclude = ["observations/"]

[dependencies]
soroban-builtin-sdk-macros = { workspace = true }
soroban-env-common = { workspace = true, features = ["std", "wasmi", "shallow-val-hash"] }
wasmi = { workspace = true }
soroban-env-common = { workspace = true, features = ["std", "shallow-val-hash"] }
wasmi = { workspace = true, optional = true }
wasmtime = { workspace = true, optional = true }
wasmparser = { workspace = true }
stellar-strkey = "0.0.9"
static_assertions = "1.1.0"
Expand Down Expand Up @@ -90,6 +91,9 @@ default-features = false
features = ["arbitrary"]

[features]
default = ["wasmi", "wasmtime"]
wasmi = ["dep:wasmi", "soroban-env-common/wasmi"]
wasmtime = ["dep:wasmtime", "soroban-env-common/wasmtime"]
testutils = ["soroban-env-common/testutils", "recording_mode"]
backtrace = ["dep:backtrace"]
next = ["soroban-env-common/next", "stellar-xdr/next"]
Expand Down
1 change: 1 addition & 0 deletions soroban-env-host/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,7 @@ impl Host {
CallParams::default_internal_call(),
);
if let Err(e) = &res {
use crate::ErrorHandler;
self.error(
e.error,
"check auth invocation for a custom account contract failed",
Expand Down
8 changes: 8 additions & 0 deletions soroban-env-host/src/budget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ mod dimension;
mod limits;
mod model;
mod util;
#[cfg(feature = "wasmi")]
mod wasmi_helper;
#[cfg(feature = "wasmtime")]
mod wasmtime_helper;

pub(crate) use limits::DepthLimiter;
pub use limits::{DEFAULT_HOST_DEPTH_LIMIT, DEFAULT_XDR_RW_LIMITS};
pub use model::{MeteredCostComponent, ScaledU64};

#[cfg(feature = "wasmi")]
pub(crate) use wasmi_helper::{get_wasmi_config, load_calibrated_fuel_costs};

#[cfg(feature = "wasmtime")]
pub(crate) use wasmtime_helper::get_wasmtime_config;

use std::{
cell::{RefCell, RefMut},
fmt::{Debug, Display},
Expand Down
15 changes: 15 additions & 0 deletions soroban-env-host/src/budget/wasmtime_helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::{budget::Budget, HostError};

pub(crate) fn get_wasmtime_config(_budget: &Budget) -> Result<wasmtime::Config, HostError> {
let mut config = wasmtime::Config::new();
config
.strategy(wasmtime::Strategy::Winch)
.debug_info(false)
.generate_address_map(false)
.consume_fuel(true)
.wasm_bulk_memory(true)
.wasm_multi_value(false)
.wasm_simd(false)
.wasm_tail_call(false);
Ok(config)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
self, AccountId, ContractIdPreimage, Hash, ScErrorCode, ScErrorType, ThresholdIndexes,
Uint256,
},
Env, EnvBase, HostError, Symbol, TryFromVal, TryIntoVal, Val,
Env, EnvBase, ErrorHandler, HostError, Symbol, TryFromVal, TryIntoVal, Val,
};
use core::cmp::Ordering;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
},
err,
host::{metered_clone::MeteredClone, Host},
Env, HostError, StorageType, TryIntoVal,
Env, ErrorHandler, HostError, StorageType, TryIntoVal,
};

use super::storage_types::AllowanceValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
LedgerEntry, LedgerEntryData, LedgerKey, ScAddress, TrustLineAsset, TrustLineEntry,
TrustLineEntryExt, TrustLineFlags,
},
Env, Host, HostError, StorageType, TryIntoVal,
Env, ErrorHandler, Host, HostError, StorageType, TryIntoVal,
};

use super::storage_types::{BalanceValue, BALANCE_EXTEND_AMOUNT, BALANCE_TTL_THRESHOLD};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
err,
host::{metered_clone::MeteredClone, Host},
xdr::Asset,
BytesObject, Compare, Env, EnvBase, HostError, TryFromVal, TryIntoVal,
BytesObject, Compare, Env, EnvBase, ErrorHandler, HostError, TryFromVal, TryIntoVal,
};

use soroban_builtin_sdk_macros::contractimpl;
Expand Down
11 changes: 7 additions & 4 deletions soroban-env-host/src/cost_runner/cost_types/vm_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ use crate::{
xdr::{ContractCostType::VmInstantiation, Hash},
Vm,
};
use std::{hint::black_box, rc::Rc};
use std::{hint::black_box, rc::Rc, sync::Arc};

#[derive(Clone)]
pub struct VmInstantiationSample {
pub id: Option<Hash>,
pub wasm: Vec<u8>,
pub module: Rc<ParsedModule>,
pub module: Arc<ParsedModule>,
}

// Protocol 20 coarse and unified cost model
Expand Down Expand Up @@ -73,7 +73,7 @@ mod v21 {

type SampleType = VmInstantiationSample;

type RecycledType = (Option<Rc<ParsedModule>>, Vec<u8>);
type RecycledType = (Option<Arc<ParsedModule>>, Vec<u8>);

fn run_iter(
host: &crate::Host,
Expand All @@ -83,7 +83,10 @@ mod v21 {
let module = black_box(
ParsedModule::new(
host,
sample.module.module.engine(),
host.get_ledger_protocol_version()
.expect("protocol version"),
sample.module.wasmi_module.engine(),
sample.module.wasmtime_module.engine(),
&sample.wasm[..],
sample.module.cost_inputs.clone(),
)
Expand Down
4 changes: 2 additions & 2 deletions soroban-env-host/src/crypto/bls12_381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::{
budget::AsBudget,
host_object::HostVec,
xdr::{ContractCostType, ScBytes, ScErrorCode, ScErrorType},
Bool, BytesObject, ConversionError, Env, Host, HostError, TryFromVal, U256Object, U256Small,
U256Val, Val, VecObject, U256,
Bool, BytesObject, ConversionError, Env, ErrorHandler, Host, HostError, TryFromVal, U256Object,
U256Small, U256Val, Val, VecObject, U256,
};
use ark_bls12_381::{
g1::Config as G1Config, g2::Config as G2Config, Bls12_381, Fq, Fq12, Fq2, Fr, G1Affine,
Expand Down
Loading
Loading