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

enhancement: compare fuel-indexer and forc-index versions #1400

Merged
merged 6 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
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(
lostman marked this conversation as resolved.
Show resolved Hide resolved
"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);
lostman marked this conversation as resolved.
Show resolved Hide resolved

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("unknown".to_string());
lostman marked this conversation as resolved.
Show resolved Hide resolved
};
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
Loading