Skip to content

Commit a92631d

Browse files
committed
Add wasmtime support and make wasmi optional.
# Conflicts: # soroban-env-host/src/vm.rs
1 parent 86ea7ea commit a92631d

21 files changed

Lines changed: 1518 additions & 102 deletions

File tree

Cargo.lock

Lines changed: 603 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ soroban-builtin-sdk-macros = { version = "=22.1.3", path = "soroban-builtin-sdk-
3232
# NB: this must match the wasmparser version wasmi is using
3333
wasmparser = "=0.116.1"
3434

35+
[workspace.dependencies.wasmtime]
36+
version = "28.0"
37+
default-features = false
38+
features = ["runtime", "winch"]
39+
git = "https://github.com/bytecodealliance/wasmtime"
40+
rev = "b5627a86a7740ffc732f4c22b9f0b2c66252638b"
41+
3542
# NB: When updating, also update the version in rs-soroban-env dev-dependencies
3643
[workspace.dependencies.stellar-xdr]
3744
version = "=22.1.0"

soroban-env-common/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ ethnum = "1.5.0"
2424
arbitrary = { version = "1.3.2", features = ["derive"], optional = true }
2525
num-traits = {version = "0.2.17", default-features = false}
2626
num-derive = "0.4.1"
27+
wasmtime = { workspace = true, optional = true}
2728

2829
[target.'cfg(not(target_family = "wasm"))'.dependencies]
2930
tracy-client = { version = "0.17.0", features = ["enable", "timer-fallback"], default-features = false, optional = true }
@@ -36,6 +37,7 @@ num-traits = "0.2.17"
3637
std = ["stellar-xdr/std", "stellar-xdr/base64"]
3738
serde = ["dep:serde", "stellar-xdr/serde"]
3839
wasmi = ["dep:wasmi", "dep:wasmparser"]
40+
wasmtime = ["dep:wasmtime"]
3941
testutils = ["dep:arbitrary", "stellar-xdr/arbitrary"]
4042
next = ["stellar-xdr/next", "soroban-env-macros/next"]
4143
tracy = ["dep:tracy-client"]

soroban-env-common/src/error.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,49 @@ impl From<wasmparser::BinaryReaderError> for Error {
283283
}
284284
}
285285

286+
#[cfg(feature = "wasmtime")]
287+
impl From<wasmtime::Trap> for Error {
288+
#[allow(clippy::wildcard_in_or_patterns)]
289+
fn from(trap: wasmtime::Trap) -> Self {
290+
let ec = match trap {
291+
wasmtime::Trap::UnreachableCodeReached => ScErrorCode::InvalidAction,
292+
293+
wasmtime::Trap::MemoryOutOfBounds | wasmtime::Trap::TableOutOfBounds => {
294+
ScErrorCode::IndexBounds
295+
}
296+
297+
wasmtime::Trap::IndirectCallToNull => ScErrorCode::MissingValue,
298+
299+
wasmtime::Trap::IntegerDivisionByZero
300+
| wasmtime::Trap::IntegerOverflow
301+
| wasmtime::Trap::BadConversionToInteger => ScErrorCode::ArithDomain,
302+
303+
wasmtime::Trap::BadSignature => ScErrorCode::UnexpectedType,
304+
305+
wasmtime::Trap::StackOverflow
306+
| wasmtime::Trap::Interrupt
307+
| wasmtime::Trap::OutOfFuel => {
308+
return Error::from_type_and_code(ScErrorType::Budget, ScErrorCode::ExceededLimit)
309+
}
310+
311+
wasmtime::Trap::HeapMisaligned
312+
| wasmtime::Trap::AlwaysTrapAdapter
313+
| wasmtime::Trap::AtomicWaitNonSharedMemory
314+
| wasmtime::Trap::NullReference
315+
| wasmtime::Trap::CannotEnterComponent
316+
| _ => ScErrorCode::InvalidAction,
317+
};
318+
Error::from_type_and_code(ScErrorType::WasmVm, ec)
319+
}
320+
}
321+
322+
#[cfg(feature = "wasmtime")]
323+
impl From<wasmtime::MemoryAccessError> for Error {
324+
fn from(_: wasmtime::MemoryAccessError) -> Self {
325+
Error::from_type_and_code(ScErrorType::WasmVm, ScErrorCode::IndexBounds)
326+
}
327+
}
328+
286329
impl Error {
287330
// NB: we don't provide a "get_type" to avoid casting a bad bit-pattern into
288331
// an ScErrorType. Instead we provide an "is_type" to check any specific

soroban-env-common/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ pub use val::{ConversionError, Tag, Val};
113113

114114
#[cfg(feature = "wasmi")]
115115
pub use val::WasmiMarshal;
116+
#[cfg(feature = "wasmtime")]
117+
pub use val::WasmtimeMarshal;
116118
pub use val::{AddressObject, MapObject, VecObject};
117119
pub use val::{Bool, Void};
118120

soroban-env-common/src/val.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,62 @@ impl WasmiMarshal for i64 {
465465
}
466466
}
467467

468+
#[cfg(feature = "wasmtime")]
469+
pub trait WasmtimeMarshal: Sized {
470+
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self>;
471+
fn marshal_wasmtime_from_self(self) -> wasmtime::Val;
472+
}
473+
474+
#[cfg(feature = "wasmtime")]
475+
impl WasmtimeMarshal for Val {
476+
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self> {
477+
if let wasmtime::Val::I64(i) = v {
478+
let v = Val::from_payload(i as u64);
479+
if v.is_good() {
480+
Some(v)
481+
} else {
482+
None
483+
}
484+
} else {
485+
None
486+
}
487+
}
488+
489+
fn marshal_wasmtime_from_self(self) -> wasmtime::Val {
490+
wasmtime::Val::I64(self.get_payload() as i64)
491+
}
492+
}
493+
494+
#[cfg(feature = "wasmtime")]
495+
impl WasmtimeMarshal for u64 {
496+
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self> {
497+
if let wasmtime::Val::I64(i) = v {
498+
Some(i as u64)
499+
} else {
500+
None
501+
}
502+
}
503+
504+
fn marshal_wasmtime_from_self(self) -> wasmtime::Val {
505+
wasmtime::Val::I64(self as i64)
506+
}
507+
}
508+
509+
#[cfg(feature = "wasmtime")]
510+
impl WasmtimeMarshal for i64 {
511+
fn try_marshal_from_wasmtime_value(v: wasmtime::Val) -> Option<Self> {
512+
if let wasmtime::Val::I64(i) = v {
513+
Some(i)
514+
} else {
515+
None
516+
}
517+
}
518+
519+
fn marshal_wasmtime_from_self(self) -> wasmtime::Val {
520+
wasmtime::Val::I64(self)
521+
}
522+
}
523+
468524
// Manually implement all the residual pieces: ValConverts
469525
// and Froms.
470526

soroban-env-common/src/vmcaller_env.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,38 +25,67 @@ use core::marker::PhantomData;
2525
/// allows code to import and use `Env` directly (such as the native
2626
/// contract) to call host methods without having to write `VmCaller::none()`
2727
/// everywhere.
28-
#[cfg(feature = "wasmi")]
29-
pub struct VmCaller<'a, T>(pub Option<wasmi::Caller<'a, T>>);
30-
#[cfg(feature = "wasmi")]
28+
29+
#[cfg(any(feature = "wasmi", feature = "wasmtime"))]
30+
pub enum VmCaller<'a, T> {
31+
#[cfg(feature = "wasmi")]
32+
WasmiCaller(wasmi::Caller<'a, T>),
33+
#[cfg(feature = "wasmtime")]
34+
WasmtimeCaller(wasmtime::Caller<'a, T>),
35+
NoCaller,
36+
}
37+
#[cfg(any(feature = "wasmi", feature = "wasmtime"))]
3138
impl<'a, T> VmCaller<'a, T> {
3239
pub fn none() -> Self {
33-
VmCaller(None)
40+
VmCaller::NoCaller
3441
}
42+
#[cfg(feature = "wasmi")]
3543
pub fn try_ref(&self) -> Result<&wasmi::Caller<'a, T>, Error> {
36-
match &self.0 {
37-
Some(caller) => Ok(caller),
38-
None => Err(Error::from_type_and_code(
44+
match self {
45+
VmCaller::WasmiCaller(caller) => Ok(caller),
46+
_ => Err(Error::from_type_and_code(
3947
ScErrorType::Context,
4048
ScErrorCode::InternalError,
4149
)),
4250
}
4351
}
52+
#[cfg(feature = "wasmi")]
4453
pub fn try_mut(&mut self) -> Result<&mut wasmi::Caller<'a, T>, Error> {
45-
match &mut self.0 {
46-
Some(caller) => Ok(caller),
47-
None => Err(Error::from_type_and_code(
54+
match self {
55+
VmCaller::WasmiCaller(caller) => Ok(caller),
56+
_ => Err(Error::from_type_and_code(
57+
ScErrorType::Context,
58+
ScErrorCode::InternalError,
59+
)),
60+
}
61+
}
62+
#[cfg(feature = "wasmtime")]
63+
pub fn try_ref_wasmtime(&self) -> Result<&wasmtime::Caller<'a, T>, Error> {
64+
match self {
65+
VmCaller::WasmtimeCaller(caller) => Ok(caller),
66+
_ => Err(Error::from_type_and_code(
67+
ScErrorType::Context,
68+
ScErrorCode::InternalError,
69+
)),
70+
}
71+
}
72+
#[cfg(feature = "wasmtime")]
73+
pub fn try_mut_wasmtime(&mut self) -> Result<&mut wasmtime::Caller<'a, T>, Error> {
74+
match self {
75+
VmCaller::WasmtimeCaller(caller) => Ok(caller),
76+
_ => Err(Error::from_type_and_code(
4877
ScErrorType::Context,
4978
ScErrorCode::InternalError,
5079
)),
5180
}
5281
}
5382
}
5483

55-
#[cfg(not(feature = "wasmi"))]
84+
#[cfg(not(any(feature = "wasmi", feature = "wasmtime")))]
5685
pub struct VmCaller<'a, T> {
5786
_nothing: PhantomData<&'a T>,
5887
}
59-
#[cfg(not(feature = "wasmi"))]
88+
#[cfg(not(any(feature = "wasmi", feature = "wasmtime")))]
6089
impl<'a, T> VmCaller<'a, T> {
6190
pub fn none() -> Self {
6291
VmCaller {

soroban-env-host/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ exclude = ["observations/"]
1515

1616
[dependencies]
1717
soroban-builtin-sdk-macros = { workspace = true }
18-
soroban-env-common = { workspace = true, features = ["std", "wasmi", "shallow-val-hash"] }
19-
wasmi = { workspace = true }
18+
soroban-env-common = { workspace = true, features = ["std", "shallow-val-hash"] }
19+
wasmi = { workspace = true, optional = true }
20+
wasmtime = { workspace = true, optional = true }
2021
wasmparser = { workspace = true }
2122
stellar-strkey = "0.0.9"
2223
static_assertions = "1.1.0"
@@ -90,6 +91,9 @@ default-features = false
9091
features = ["arbitrary"]
9192

9293
[features]
94+
default = ["wasmi", "wasmtime"]
95+
wasmi = ["dep:wasmi", "soroban-env-common/wasmi"]
96+
wasmtime = ["dep:wasmtime", "soroban-env-common/wasmtime"]
9397
testutils = ["soroban-env-common/testutils", "recording_mode"]
9498
backtrace = ["dep:backtrace"]
9599
next = ["soroban-env-common/next", "stellar-xdr/next"]

soroban-env-host/src/budget.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ mod dimension;
22
mod limits;
33
mod model;
44
mod util;
5+
#[cfg(feature = "wasmi")]
56
mod wasmi_helper;
7+
#[cfg(feature = "wasmtime")]
8+
mod wasmtime_helper;
69

710
pub(crate) use limits::DepthLimiter;
811
pub use limits::{DEFAULT_HOST_DEPTH_LIMIT, DEFAULT_XDR_RW_LIMITS};
912
pub use model::{MeteredCostComponent, ScaledU64};
13+
14+
#[cfg(feature = "wasmi")]
1015
pub(crate) use wasmi_helper::{get_wasmi_config, load_calibrated_fuel_costs};
1116

17+
#[cfg(feature = "wasmtime")]
18+
pub(crate) use wasmtime_helper::get_wasmtime_config;
19+
1220
use std::{
1321
cell::{RefCell, RefMut},
1422
fmt::{Debug, Display},
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use crate::{budget::Budget, HostError};
2+
3+
pub(crate) fn get_wasmtime_config(_budget: &Budget) -> Result<wasmtime::Config, HostError> {
4+
let mut config = wasmtime::Config::new();
5+
config
6+
.strategy(wasmtime::Strategy::Winch)
7+
.debug_info(false)
8+
.generate_address_map(false)
9+
.consume_fuel(true)
10+
.wasm_bulk_memory(true)
11+
.wasm_multi_value(false)
12+
.wasm_simd(false)
13+
.wasm_tail_call(false);
14+
Ok(config)
15+
}

0 commit comments

Comments
 (0)