Skip to content

Commit a0485eb

Browse files
committed
Add Rust ABI support for WASM subgraphs
1 parent 019c4c0 commit a0485eb

8 files changed

Lines changed: 1286 additions & 7 deletions

File tree

runtime/wasm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod asc_abi;
2+
pub mod rust_abi;
23

34
mod host;
45
pub mod to_from;

runtime/wasm/src/mapping.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ pub struct ValidModule {
258258
/// Cache for asc_type_id results. Maps IndexForAscTypeId to their WASM runtime
259259
/// type IDs. Populated lazily on first use; deterministic per compiled module.
260260
asc_type_id_cache: RwLock<HashMap<IndexForAscTypeId, u32>>,
261+
262+
/// The mapping language (AssemblyScript or Rust) detected from module imports.
263+
pub language: crate::rust_abi::MappingLanguage,
261264
}
262265

263266
impl ValidModule {
@@ -355,7 +358,7 @@ impl ValidModule {
355358
epoch_counter_abort_handle = Some(graph::spawn(epoch_counter).abort_handle());
356359
}
357360

358-
let linker = crate::module::build_linker(engine, &import_name_to_modules)?;
361+
let (linker, language) = crate::module::build_linker(engine, &import_name_to_modules)?;
359362
let instance_pre = linker.instantiate_pre(&module)?;
360363

361364
Ok(ValidModule {
@@ -366,6 +369,7 @@ impl ValidModule {
366369
timeout,
367370
epoch_counter_abort_handle,
368371
asc_type_id_cache: RwLock::new(HashMap::new()),
372+
language,
369373
})
370374
}
371375

runtime/wasm/src/module/context.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,3 +1262,174 @@ fn truncate_yaml_bytes_for_logging(bytes: &[u8]) -> String {
12621262

12631263
format!("0x{}", hex::encode(bytes))
12641264
}
1265+
1266+
// ============================================================================
1267+
// Rust ABI methods
1268+
//
1269+
// These methods are used by the Rust ABI host functions in rust_abi/host.rs.
1270+
// They take pre-extracted values rather than AscPtr types.
1271+
// ============================================================================
1272+
1273+
impl WasmInstanceContext<'_> {
1274+
/// Rust ABI: store.set
1275+
pub async fn rust_store_set(
1276+
&mut self,
1277+
gas: &GasCounter,
1278+
entity_type: &str,
1279+
id: &str,
1280+
data: std::collections::HashMap<String, store::Value>,
1281+
) -> Result<(), HostExportError> {
1282+
let stopwatch = self.as_ref().host_metrics.stopwatch.cheap_clone();
1283+
let logger = self.as_ref().ctx.logger.cheap_clone();
1284+
1285+
if self.as_ref().ctx.instrument {
1286+
debug!(self.as_ref().ctx.logger, "rust_store_set";
1287+
"type" => entity_type,
1288+
"id" => id);
1289+
}
1290+
1291+
// Convert HashMap<String, Value> to HashMap<Word, Value>
1292+
let data: std::collections::HashMap<Word, store::Value> = data
1293+
.into_iter()
1294+
.map(|(k, v)| (Word::from(k), v))
1295+
.collect();
1296+
1297+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1298+
let ctx = &mut self.as_mut().ctx;
1299+
1300+
host_exports
1301+
.store_set(
1302+
&logger,
1303+
&mut ctx.state,
1304+
&ctx.proof_of_indexing,
1305+
ctx.timestamp,
1306+
entity_type.to_string(),
1307+
id.to_string(),
1308+
data,
1309+
&stopwatch,
1310+
gas,
1311+
)
1312+
.await?;
1313+
1314+
Ok(())
1315+
}
1316+
1317+
/// Rust ABI: store.get - returns serialized entity bytes or None
1318+
pub async fn rust_store_get(
1319+
&mut self,
1320+
gas: &GasCounter,
1321+
entity_type: &str,
1322+
id: &str,
1323+
) -> Result<Option<Vec<u8>>, HostExportError> {
1324+
use crate::rust_abi::serialize_entity;
1325+
1326+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1327+
let _timer = self
1328+
.as_ref()
1329+
.host_metrics
1330+
.cheap_clone()
1331+
.time_host_fn_execution_region("rust_store_get");
1332+
1333+
let entity_option = host_exports
1334+
.store_get(
1335+
&mut self.as_mut().ctx.state,
1336+
entity_type.to_string(),
1337+
id.to_string(),
1338+
gas,
1339+
graph::components::store::GetScope::Store,
1340+
)
1341+
.await?;
1342+
1343+
if self.as_ref().ctx.instrument {
1344+
debug!(self.as_ref().ctx.logger, "rust_store_get";
1345+
"type" => entity_type,
1346+
"id" => id,
1347+
"found" => entity_option.is_some());
1348+
}
1349+
1350+
match entity_option {
1351+
Some(entity) => {
1352+
let bytes = serialize_entity(&entity);
1353+
Ok(Some(bytes))
1354+
}
1355+
None => Ok(None),
1356+
}
1357+
}
1358+
1359+
/// Rust ABI: store.remove
1360+
pub async fn rust_store_remove(
1361+
&mut self,
1362+
gas: &GasCounter,
1363+
entity_type: &str,
1364+
id: &str,
1365+
) -> Result<(), HostExportError> {
1366+
let logger = self.as_ref().ctx.logger.cheap_clone();
1367+
1368+
if self.as_ref().ctx.instrument {
1369+
debug!(self.as_ref().ctx.logger, "rust_store_remove";
1370+
"type" => entity_type,
1371+
"id" => id);
1372+
}
1373+
1374+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1375+
let ctx = &mut self.as_mut().ctx;
1376+
1377+
host_exports.store_remove(
1378+
&logger,
1379+
&mut ctx.state,
1380+
&ctx.proof_of_indexing,
1381+
entity_type.to_string(),
1382+
id.to_string(),
1383+
gas,
1384+
)
1385+
}
1386+
1387+
/// Rust ABI: log.log
1388+
pub async fn rust_log(
1389+
&mut self,
1390+
gas: &GasCounter,
1391+
level: u32,
1392+
message: &str,
1393+
) -> Result<(), HostExportError> {
1394+
// Convert u32 level to slog::Level
1395+
let slog_level = match level {
1396+
0 => slog::Level::Debug,
1397+
1 => slog::Level::Info,
1398+
2 => slog::Level::Warning,
1399+
3 => slog::Level::Error,
1400+
_ => slog::Level::Critical,
1401+
};
1402+
1403+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1404+
let ctx = &mut self.as_mut().ctx;
1405+
1406+
host_exports
1407+
.log_log(&ctx.logger, slog_level, message.to_string(), gas, &mut ctx.state)
1408+
.map_err(|e| HostExportError::Deterministic(e.into()))
1409+
}
1410+
1411+
/// Rust ABI: dataSource.address
1412+
pub async fn rust_data_source_address(
1413+
&mut self,
1414+
gas: &GasCounter,
1415+
) -> Result<Vec<u8>, HostExportError> {
1416+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1417+
let ctx = &mut self.as_mut().ctx;
1418+
1419+
let address = host_exports.data_source_address(gas, &mut ctx.state)?;
1420+
Ok(address.to_vec())
1421+
}
1422+
1423+
/// Rust ABI: dataSource.network
1424+
pub async fn rust_data_source_network(
1425+
&mut self,
1426+
gas: &GasCounter,
1427+
) -> Result<String, HostExportError> {
1428+
let host_exports = self.as_ref().ctx.host_exports.cheap_clone();
1429+
let ctx = &mut self.as_mut().ctx;
1430+
1431+
host_exports
1432+
.data_source_network(gas, &mut ctx.state)
1433+
.map_err(|e| HostExportError::Deterministic(e.into()))
1434+
}
1435+
}

0 commit comments

Comments
 (0)