Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

Commit

Permalink
enhancement: compare fuel-indexer and forc-index versions (#1400)
Browse files Browse the repository at this point in the history
* compare fuel-indexer and forc-index versions

* instantiate externs for version check

* comment

* update simple_wasm.wasm

* add --disable-toolchain-version-check flag

* Update packages/fuel-indexer-api-server/src/uses.rs

Co-authored-by: rashad <[email protected]>

---------

Co-authored-by: rashad <[email protected]>
  • Loading branch information
lostman and ra0x3 authored Oct 11, 2023
1 parent 604f71d commit c638f2d
Show file tree
Hide file tree
Showing 16 changed files with 155 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/fuel-indexer-api-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tower = { version = "0.4", features = ["limit", "buffer"] }
tower-http = { version = "0.3", features = ["fs", "trace", "cors", "limit"] }
tracing = { workspace = true }
wasmer = "4"

[features]
default = ["metrics"]
Expand Down
8 changes: 8 additions & 0 deletions packages/fuel-indexer-api-server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ pub enum ApiError {
SqlValidator(#[from] crate::sql::SqlValidatorError),
#[error("ParseError: {0:?}")]
ParseError(#[from] strum::ParseError),
#[error("The forc-index version {toolchain_version} does not match the fuel-indexer version {fuel_indexer_version}.")]
ToolchainVersionMismatch {
toolchain_version: String,
fuel_indexer_version: String,
},
#[error("Other error: {0}")]
OtherError(String),
}
Expand Down Expand Up @@ -168,6 +173,9 @@ impl IntoResponse for ApiError {
// This is currently the only type of ParseError on the web server
(StatusCode::BAD_REQUEST, format!("Invalid asset type: {e}"))
}
ApiError::ToolchainVersionMismatch{fuel_indexer_version, toolchain_version} => {
(StatusCode::METHOD_NOT_ALLOWED, format!("WASM module toolchain version `{toolchain_version}` does not match fuel-indexer version `{fuel_indexer_version}`"))
}
_ => (StatusCode::INTERNAL_SERVER_ERROR, generic_details),
};

Expand Down
75 changes: 75 additions & 0 deletions packages/fuel-indexer-api-server/src/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use wasmer::{
imports, AsStoreMut, Exports, Function, Instance, MemoryView, StoreMut, WasmPtr,
};

/// Extract the `TOOLCHAIN_VERSION` string from a WASM module. This function
/// creates a `wasmer::Instance` in order to do this.
pub(crate) fn check_wasm_toolchain_version(data: Vec<u8>) -> anyhow::Result<String> {
let mut store = wasmer::Store::default();

let module = wasmer::Module::new(&store, data.clone())?;

let mut exports = Exports::new();
exports.insert(
"ff_put_object".to_string(),
Function::new_typed(&mut store, |_: i64, _: i32, _: i32| {}),
);
exports.insert(
"ff_get_object".to_string(),
Function::new_typed(&mut store, |_: i64, _: i32, _: i32| 0i32),
);
exports.insert(
"ff_early_exit".to_string(),
Function::new_typed(&mut store, |_: i32| {}),
);
exports.insert(
"ff_put_many_to_many_record".to_string(),
Function::new_typed(&mut store, |_: i32, _: i32| {}),
);
exports.insert(
"ff_log_data".to_string(),
Function::new_typed(&mut store, |_: i32, _: i32, _: i32| {}),
);

let mut imports = imports! {};
wasmer::Imports::register_namespace(&mut imports, "env", exports);

let instance = wasmer::Instance::new(&mut store, &module, &imports)?;

let version = get_toolchain_version(&mut store.as_store_mut(), &instance)?;

Ok(version)
}

/// Get the toolchain version stored in the WASM module.
pub fn get_toolchain_version(
store: &mut StoreMut,
instance: &Instance,
) -> anyhow::Result<String> {
let exports = &instance.exports;

let ptr = exports
.get_function("get_toolchain_version_ptr")?
.call(store, &[])?[0]
.i32()
.ok_or_else(|| anyhow::anyhow!("get_toolchain_version_ptr".to_string()))?
as u32;

let len = exports
.get_function("get_toolchain_version_len")?
.call(store, &[])?[0]
.i32()
.ok_or_else(|| anyhow::anyhow!("get_toolchain_version_len".to_string()))?
as u32;

let memory = exports.get_memory("memory")?.view(store);
let version = get_string(&memory, ptr, len)?;

Ok(version)
}

/// Fetch the string at the given pointer from memory.
fn get_string(mem: &MemoryView, ptr: u32, len: u32) -> anyhow::Result<String> {
let result = WasmPtr::<u8>::new(ptr).read_utf8_string(mem, len)?;
Ok(result)
}
1 change: 1 addition & 0 deletions packages/fuel-indexer-api-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod api;
pub mod cli;
pub(crate) mod commands;
pub(crate) mod ffi;
pub(crate) mod middleware;
pub(crate) mod models;
pub(crate) mod sql;
Expand Down
29 changes: 26 additions & 3 deletions packages/fuel-indexer-api-server/src/uses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,20 @@ pub(crate) async fn register_indexer_assets(

let multipart = multipart.ok_or_else(ApiError::default)?;

let (replace_indexer, asset_bytes) =
let (toolchain_version, replace_indexer, asset_bytes) =
parse_register_indexer_multipart(multipart).await?;

let fuel_indexer_version = env!("CARGO_PKG_VERSION").to_string();

if !config.disable_toolchain_version_check
&& toolchain_version != fuel_indexer_version
{
return Err(ApiError::ToolchainVersionMismatch {
toolchain_version,
fuel_indexer_version,
});
}

queries::start_transaction(&mut conn).await?;

let result = register_indexer_assets_transaction(
Expand Down Expand Up @@ -374,7 +385,8 @@ async fn register_indexer_assets_transaction(
// schema, and the WASM module.
async fn parse_register_indexer_multipart(
mut multipart: Multipart,
) -> ApiResult<(bool, Vec<(IndexerAssetType, Vec<u8>)>)> {
) -> ApiResult<(String, bool, Vec<(IndexerAssetType, Vec<u8>)>)> {
let mut toolchain_version: String = "unknown".to_string();
let mut replace_indexer: bool = false;
let mut assets: Vec<(IndexerAssetType, Vec<u8>)> = vec![];

Expand All @@ -390,12 +402,23 @@ async fn parse_register_indexer_multipart(
}
name => {
let asset_type = IndexerAssetType::from_str(name)?;
if asset_type == IndexerAssetType::Wasm {
toolchain_version =
crate::ffi::check_wasm_toolchain_version(data.clone().into())
.map_err(|e| {
tracing::warn!(
"Failed to get WASM module toolchain version: {e}"
);
e
})
.unwrap_or(toolchain_version);
};
assets.push((asset_type, data.to_vec()));
}
};
}

Ok((replace_indexer, assets))
Ok((toolchain_version, replace_indexer, assets))
}

/// Return a `Nonce` to be used for authentication.
Expand Down
13 changes: 13 additions & 0 deletions packages/fuel-indexer-lib/src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ pub struct IndexerArgs {
help = "Allow missing blocks or non-sequential block processing."
)]
pub allow_non_sequential_blocks: bool,

/// By default, Fuel Indexer will only accept WASM indexer modules compiled with the same toolchain version as the version of Fuel Indexer.
#[clap(
long,
help = "By default, Fuel Indexer will only accept WASM indexer modules compiled with the same toolchain version as the version of Fuel Indexer."
)]
pub disable_toolchain_version_check: bool,
}

#[derive(Debug, Parser, Clone)]
Expand Down Expand Up @@ -323,4 +330,10 @@ pub struct ApiServerArgs {
/// Allow the web server to accept raw SQL queries.
#[clap(long, help = "Allow the web server to accept raw SQL queries.")]
pub accept_sql_queries: bool,
/// By default, Fuel Indexer will only accept WASM indexer modules compiled with the same toolchain version as the version of Fuel Indexer.
#[clap(
long,
help = "By default, Fuel Indexer will only accept WASM indexer modules compiled with the same toolchain version as the version of Fuel Indexer."
)]
pub disable_toolchain_version_check: bool,
}
5 changes: 5 additions & 0 deletions packages/fuel-indexer-lib/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ impl Default for IndexerArgs {
accept_sql_queries: defaults::ACCEPT_SQL,
block_page_size: defaults::NODE_BLOCK_PAGE_SIZE,
allow_non_sequential_blocks: defaults::ALLOW_NON_SEQUENTIAL_BLOCKS,
disable_toolchain_version_check: defaults::DISABLE_TOOLCHAIN_VERSION_CHECK,
}
}
}
Expand Down Expand Up @@ -138,6 +139,7 @@ pub struct IndexerConfig {
pub accept_sql_queries: bool,
pub block_page_size: usize,
pub allow_non_sequential_blocks: bool,
pub disable_toolchain_version_check: bool,
}

impl Default for IndexerConfig {
Expand All @@ -160,6 +162,7 @@ impl Default for IndexerConfig {
accept_sql_queries: defaults::ACCEPT_SQL,
block_page_size: defaults::NODE_BLOCK_PAGE_SIZE,
allow_non_sequential_blocks: defaults::ALLOW_NON_SEQUENTIAL_BLOCKS,
disable_toolchain_version_check: defaults::DISABLE_TOOLCHAIN_VERSION_CHECK,
}
}
}
Expand Down Expand Up @@ -242,6 +245,7 @@ impl From<IndexerArgs> for IndexerConfig {
accept_sql_queries: args.accept_sql_queries,
block_page_size: args.block_page_size,
allow_non_sequential_blocks: args.allow_non_sequential_blocks,
disable_toolchain_version_check: args.disable_toolchain_version_check,
};

config
Expand Down Expand Up @@ -330,6 +334,7 @@ impl From<ApiServerArgs> for IndexerConfig {
accept_sql_queries: args.accept_sql_queries,
block_page_size: defaults::NODE_BLOCK_PAGE_SIZE,
allow_non_sequential_blocks: defaults::ALLOW_NON_SEQUENTIAL_BLOCKS,
disable_toolchain_version_check: args.disable_toolchain_version_check,
};

config
Expand Down
3 changes: 3 additions & 0 deletions packages/fuel-indexer-lib/src/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,6 @@ pub const ACCEPT_SQL: bool = false;

/// Allow missing blocks or non-sequential block processing.
pub const ALLOW_NON_SEQUENTIAL_BLOCKS: bool = false;

/// By default, Fuel Indexer will only accept WASM indexer modules compiled with the same toolchain version as the version of Fuel Indexer.
pub const DISABLE_TOOLCHAIN_VERSION_CHECK: bool = false;
4 changes: 4 additions & 0 deletions packages/fuel-indexer-macros/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,14 @@ pub(crate) fn process_graphql_schema(

let version_tokens = const_item("VERSION", schema.version());

let toolchain_version_tokens =
const_item("TOOLCHAIN_VERSION", env!("CARGO_PKG_VERSION"));

let mut output = quote! {
#namespace_tokens
#identifer_tokens
#version_tokens
#toolchain_version_tokens
};

let schema =
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ replace_indexer: false
accept_sql_queries: false
block_page_size: 20
allow_non_sequential_blocks: false
disable_toolchain_version_check: false

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ OPTIONS:
--database <DATABASE>
Database type. [default: postgres] [possible values: postgres]

--disable-toolchain-version-check
By default, Fuel Indexer will only accept WASM indexer modules compiled with the same
toolchain version as the version of Fuel Indexer.

--embedded-database
Automatically create and start database using provided options or defaults.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ OPTIONS:
--database <DATABASE>
Database type. [default: postgres] [possible values: postgres]

--disable-toolchain-version-check
By default, Fuel Indexer will only accept WASM indexer modules compiled with the same
toolchain version as the version of Fuel Indexer.

--fuel-node-host <FUEL_NODE_HOST>
Host of the running Fuel node. [default: localhost]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ OPTIONS:
--database <DATABASE>
Database type. [default: postgres] [possible values: postgres]

--disable-toolchain-version-check
By default, Fuel Indexer will only accept WASM indexer modules compiled with the same
toolchain version as the version of Fuel Indexer.

--embedded-database
Automatically create and start database using provided options or defaults.

Expand Down
5 changes: 5 additions & 0 deletions plugins/forc-index/src/ops/forc_index_start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub async fn init(command: StartCommand) -> anyhow::Result<()> {
accept_sql_queries,
block_page_size,
allow_non_sequential_blocks,
disable_toolchain_version_check,
} = command;

let mut cmd = Command::new("fuel-indexer");
Expand Down Expand Up @@ -93,6 +94,10 @@ pub async fn init(command: StartCommand) -> anyhow::Result<()> {
("--verbose", verbose),
("--local-fuel-node", local_fuel_node),
("--allow-non-sequential-blocks", allow_non_sequential_blocks),
(
"--disable-toolchain-version-check",
disable_toolchain_version_check,
),
];
for (opt, value) in options.iter() {
if *value {
Expand Down

0 comments on commit c638f2d

Please sign in to comment.