From 5819f1cf39eb390da8ab9b385ad96c4e5527d233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Wo=C5=9B?= Date: Mon, 23 Oct 2023 16:46:36 +0100 Subject: [PATCH 1/2] support Result in indexers --- packages/fuel-indexer-macros/src/indexer.rs | 42 +++++++++++++++++++-- packages/fuel-indexer-macros/src/wasm.rs | 13 ++++--- packages/fuel-indexer/src/executor.rs | 15 ++++---- packages/fuel-indexer/src/ffi.rs | 6 +-- 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/packages/fuel-indexer-macros/src/indexer.rs b/packages/fuel-indexer-macros/src/indexer.rs index ef3182e3d..c99aba6c1 100644 --- a/packages/fuel-indexer-macros/src/indexer.rs +++ b/packages/fuel-indexer-macros/src/indexer.rs @@ -561,6 +561,7 @@ fn process_fn_items( } let fn_name = &fn_item.sig.ident; + let fn_name_string = fn_name.to_string(); if arg_list.is_empty() { proc_macro_error::abort_call_site!( @@ -569,9 +570,26 @@ fn process_fn_items( ); } + let fn_call = if fn_item.sig.output == syn::ReturnType::Default { + quote! { + #fn_name(#(#arg_list),*)#awaitness + } + } else { + quote! { + if let Err(e) = #fn_name(#(#arg_list),*)#awaitness { + unsafe { + if !ERROR_MESSAGE.is_empty() { + ERROR_MESSAGE += "\n"; + } + ERROR_MESSAGE += &format!("{} failed with an error: {}", #fn_name_string, e.to_string()); + } + } + } + }; + abi_dispatchers.push(quote! { if ( #(#input_checks)&&* ) { - #fn_name(#(#arg_list),*)#awaitness; + #fn_call; } }); @@ -586,6 +604,23 @@ fn process_fn_items( } } + let error_message_handler = match manifest.execution_source() { + ExecutionSource::Native => { + quote! {} + } + ExecutionSource::Wasm => { + quote! { + unsafe { + if !ERROR_MESSAGE.is_empty() { + let message = ERROR_MESSAGE.lines().map(|l| format!(" {l}")).collect::>().join("\n"); + ERROR_MESSAGE = format!("At height {}:\n", block_height) + &message; + early_exit(WasmIndexerError::GeneralError) + } + } + } + } + }; + let decoder_struct = quote! { #[derive(Default)] struct Decoders { @@ -666,8 +701,9 @@ fn process_fn_items( } } - pub #asyncness fn dispatch(&self) { + pub #asyncness fn dispatch(&self, block_height: u32) { #(#abi_dispatchers)* + #error_message_handler } } }; @@ -881,7 +917,7 @@ fn process_fn_items( } } } - decoder.dispatch()#awaitness; + decoder.dispatch(block.header.height)#awaitness; let metadata = IndexMetadataEntity::new(block.time as u64, block.header.height, block.id); metadata.save()#awaitness; diff --git a/packages/fuel-indexer-macros/src/wasm.rs b/packages/fuel-indexer-macros/src/wasm.rs index 416e9f305..ae932f716 100644 --- a/packages/fuel-indexer-macros/src/wasm.rs +++ b/packages/fuel-indexer-macros/src/wasm.rs @@ -17,6 +17,7 @@ pub fn handler_block_wasm( #[no_mangle] fn handle_events(blob: *mut u8, len: usize) { register_panic_hook(); + use fuel_indexer_utils::plugin::deserialize; let bytes = unsafe { Vec::from_raw_parts(blob, len, len) }; let blocks: Vec = match deserialize(&bytes) { @@ -41,16 +42,16 @@ pub fn handler_block_wasm( /// retrieved by the indexer service and logged. fn panic_hook() -> proc_macro2::TokenStream { quote! { - static mut PANIC_MESSAGE: String = String::new(); + static mut ERROR_MESSAGE: String = String::new(); #[no_mangle] - fn get_panic_message_ptr() -> *const u8 { - unsafe { PANIC_MESSAGE.as_ptr() } + fn get_error_message_ptr() -> *const u8 { + unsafe { ERROR_MESSAGE.as_ptr() } } #[no_mangle] - fn get_panic_message_len() -> u32 { - unsafe { PANIC_MESSAGE.len() as u32 } + fn get_error_message_len() -> u32 { + unsafe { ERROR_MESSAGE.len() as u32 } } #[no_mangle] @@ -62,7 +63,7 @@ fn panic_hook() -> proc_macro2::TokenStream { SET_HOOK.call_once(|| { panic::set_hook(Box::new(|info| { unsafe { - PANIC_MESSAGE = info.to_string(); + ERROR_MESSAGE = info.to_string(); } early_exit(WasmIndexerError::Panic); })); diff --git a/packages/fuel-indexer/src/executor.rs b/packages/fuel-indexer/src/executor.rs index 00fbe7fa3..3aa2df8ca 100644 --- a/packages/fuel-indexer/src/executor.rs +++ b/packages/fuel-indexer/src/executor.rs @@ -233,9 +233,10 @@ pub fn run_executor( Some(&WasmIndexerError::MissingBlocksError) => { return Err(anyhow::anyhow!("{e}").into()); } - Some(&WasmIndexerError::Panic) => { + Some(&WasmIndexerError::Panic) + | Some(&WasmIndexerError::GeneralError) => { let message = executor - .get_panic_message() + .get_error_message() .await .unwrap_or("unknown".to_string()); return Err(anyhow::anyhow!("{message}").into()); @@ -578,7 +579,7 @@ where fn kill_switch(&self) -> &Arc; - async fn get_panic_message(&self) -> IndexerResult; + async fn get_error_message(&self) -> IndexerResult; } /// WASM indexer runtime environment responsible for fetching/saving data to and from the database. @@ -713,9 +714,9 @@ where &self.manifest } - async fn get_panic_message(&self) -> IndexerResult { + async fn get_error_message(&self) -> IndexerResult { return Err(anyhow::anyhow!( - "get_panic_message() not supported in native exetutor." + "get_error_message() not supported in native exetutor." ) .into()); } @@ -1006,9 +1007,9 @@ impl Executor for WasmIndexExecutor { &self.manifest } - async fn get_panic_message(&self) -> IndexerResult { + async fn get_error_message(&self) -> IndexerResult { let mut store = self.store.lock().await; - let result = ffi::get_panic_message(&mut store, &self.instance)?; + let result = ffi::get_error_message(&mut store, &self.instance)?; Ok(result) } } diff --git a/packages/fuel-indexer/src/ffi.rs b/packages/fuel-indexer/src/ffi.rs index 7815605a5..a84d25266 100644 --- a/packages/fuel-indexer/src/ffi.rs +++ b/packages/fuel-indexer/src/ffi.rs @@ -64,12 +64,12 @@ fn get_string_from_instance( } /// Get the version of the indexer schema stored in the WASM instance. -pub fn get_panic_message(store: &mut Store, instance: &Instance) -> FFIResult { +pub fn get_error_message(store: &mut Store, instance: &Instance) -> FFIResult { get_string_from_instance( store, instance, - "get_panic_message_ptr", - "get_panic_message_len", + "get_error_message_ptr", + "get_error_message_len", ) } From 21a9c5a93647cd961a90a1d903e4f8b2bbb3b372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Wo=C5=9B?= Date: Tue, 24 Oct 2023 12:45:52 +0100 Subject: [PATCH 2/2] cargo fmt --- packages/fuel-indexer-macros/src/indexer.rs | 382 ++++++++++---------- 1 file changed, 191 insertions(+), 191 deletions(-) diff --git a/packages/fuel-indexer-macros/src/indexer.rs b/packages/fuel-indexer-macros/src/indexer.rs index 805f7cb8b..2e8f37ec5 100644 --- a/packages/fuel-indexer-macros/src/indexer.rs +++ b/packages/fuel-indexer-macros/src/indexer.rs @@ -694,221 +694,221 @@ fn process_fn_items( }; ( quote! { - #subscribed_contract_ids + #subscribed_contract_ids - for block in blocks { + for block in blocks { - #start_block + #start_block - let mut decoder = Decoders::default(); + let mut decoder = Decoders::default(); - let ty_id = BlockData::type_id(); - let data = serialize(&block); - decoder.decode_type(ty_id, data); + let ty_id = BlockData::type_id(); + let data = serialize(&block); + decoder.decode_type(ty_id, data); - for tx in block.transactions { + for tx in block.transactions { - let mut return_types = Vec::new(); - let mut callees = HashSet::new(); + let mut return_types = Vec::new(); + let mut callees = HashSet::new(); - for receipt in tx.receipts { - match receipt { - fuel::Receipt::Call { id: contract_id, amount, asset_id, gas, param1, to: id, .. } => { - #check_if_subscribed_to_contract + for receipt in tx.receipts { + match receipt { + fuel::Receipt::Call { id: contract_id, amount, asset_id, gas, param1, to: id, .. } => { + #check_if_subscribed_to_contract - let fn_name = decoder.selector_to_fn_name(param1); - return_types.push(param1); - callees.insert(id); + let fn_name = decoder.selector_to_fn_name(param1); + return_types.push(param1); + callees.insert(id); - let data = serialize( - &Call { - contract_id: ContractId::from(<[u8; 32]>::from(contract_id)), - to: ContractId::from(<[u8; 32]>::from(id)), - amount, - asset_id: AssetId::from(<[u8; 32]>::from(asset_id)), - gas, - fn_name - } - ); - let ty_id = Call::type_id(); - decoder.decode_type(ty_id, data); + let data = serialize( + &Call { + contract_id: ContractId::from(<[u8; 32]>::from(contract_id)), + to: ContractId::from(<[u8; 32]>::from(id)), + amount, + asset_id: AssetId::from(<[u8; 32]>::from(asset_id)), + gas, + fn_name } - fuel::Receipt::Log { id, ra, rb, .. } => { - #check_if_subscribed_to_contract - let ty_id = Log::type_id(); - let data = serialize( - &Log { - contract_id: ContractId::from(<[u8; 32]>::from(id)), - ra, - rb - } - ); - decoder.decode_type(ty_id, data); - } - fuel::Receipt::LogData { rb, data, ptr, len, id, .. } => { - #check_if_subscribed_to_contract - decoder.decode_logdata(rb as usize, data.unwrap_or(Vec::::new())); + ); + let ty_id = Call::type_id(); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::Log { id, ra, rb, .. } => { + #check_if_subscribed_to_contract + let ty_id = Log::type_id(); + let data = serialize( + &Log { + contract_id: ContractId::from(<[u8; 32]>::from(id)), + ra, + rb } - fuel::Receipt::Return { id, val, pc, is } => { - #check_if_subscribed_to_contract - if callees.contains(&id) { - let ty_id = Return::type_id(); - let data = serialize( - &Return { - contract_id: ContractId::from(<[u8; 32]>::from(id)), - val, - pc, - is - } - ); - decoder.decode_type(ty_id, data); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::LogData { rb, data, ptr, len, id, .. } => { + #check_if_subscribed_to_contract + decoder.decode_logdata(rb as usize, data.unwrap_or(Vec::::new())); + } + fuel::Receipt::Return { id, val, pc, is } => { + #check_if_subscribed_to_contract + if callees.contains(&id) { + let ty_id = Return::type_id(); + let data = serialize( + &Return { + contract_id: ContractId::from(<[u8; 32]>::from(id)), + val, + pc, + is } - } - fuel::Receipt::ReturnData { data, id, .. } => { - #check_if_subscribed_to_contract - if callees.contains(&id) { - let selector = return_types.pop().expect("No return type available. <('-'<)"); - decoder.decode_return_type(selector, data.unwrap_or(Vec::::new())); + ); + decoder.decode_type(ty_id, data); + } + } + fuel::Receipt::ReturnData { data, id, .. } => { + #check_if_subscribed_to_contract + if callees.contains(&id) { + let selector = return_types.pop().expect("No return type available. <('-'<)"); + decoder.decode_return_type(selector, data.unwrap_or(Vec::::new())); + } + } + fuel::Receipt::MessageOut { sender, recipient, amount, nonce, len, digest, data, .. } => { + let sender = Address::from(<[u8; 32]>::from(sender)); + let recipient = Address::from(<[u8; 32]>::from(recipient)); + let message_id = decoder.compute_message_id(&sender, &recipient, nonce, amount, data.clone()); + + // It's possible that the data field was generated from an empty Sway `Bytes` array + // in the send_message() instruction in which case the data field in the receipt will + // have no type information or data to decode. Thus, we check for a None value or + // an empty byte vector; if either condition is present, then we decode to a unit struct instead. + let (type_id, data) = data + .map_or((u64::MAX, Vec::::new()), |buffer| { + if buffer.is_empty() { + (u64::MAX, Vec::::new()) + } else { + let (type_id_bytes, data_bytes) = buffer.split_at(8); + let type_id = u64::from_be_bytes( + <[u8; 8]>::try_from(type_id_bytes) + .expect("Could not get type ID for data in MessageOut receipt") + ); + let data = data_bytes.to_vec(); + (type_id, data) } + }); + + + decoder.decode_messagedata(type_id, data.clone()); + + let ty_id = MessageOut::type_id(); + let data = serialize( + &MessageOut { + message_id, + sender, + recipient, + amount, + nonce, + len, + digest, + data } - fuel::Receipt::MessageOut { sender, recipient, amount, nonce, len, digest, data, .. } => { - let sender = Address::from(<[u8; 32]>::from(sender)); - let recipient = Address::from(<[u8; 32]>::from(recipient)); - let message_id = decoder.compute_message_id(&sender, &recipient, nonce, amount, data.clone()); - - // It's possible that the data field was generated from an empty Sway `Bytes` array - // in the send_message() instruction in which case the data field in the receipt will - // have no type information or data to decode. Thus, we check for a None value or - // an empty byte vector; if either condition is present, then we decode to a unit struct instead. - let (type_id, data) = data - .map_or((u64::MAX, Vec::::new()), |buffer| { - if buffer.is_empty() { - (u64::MAX, Vec::::new()) - } else { - let (type_id_bytes, data_bytes) = buffer.split_at(8); - let type_id = u64::from_be_bytes( - <[u8; 8]>::try_from(type_id_bytes) - .expect("Could not get type ID for data in MessageOut receipt") - ); - let data = data_bytes.to_vec(); - (type_id, data) - } - }); - - - decoder.decode_messagedata(type_id, data.clone()); - - let ty_id = MessageOut::type_id(); - let data = serialize( - &MessageOut { - message_id, - sender, - recipient, - amount, - nonce, - len, - digest, - data - } - ); - decoder.decode_type(ty_id, data); - } - fuel::Receipt::ScriptResult { result, gas_used } => { - let ty_id = ScriptResult::type_id(); - let data = serialize(&ScriptResult{ result: u64::from(result), gas_used }); - decoder.decode_type(ty_id, data); - } - fuel::Receipt::Transfer { id, to, asset_id, amount, pc, is, .. } => { - #check_if_subscribed_to_contract - let ty_id = Transfer::type_id(); - let data = serialize( - &Transfer { - contract_id: ContractId::from(<[u8; 32]>::from(id)), - to: ContractId::from(<[u8; 32]>::from(to)), - asset_id: AssetId::from(<[u8; 32]>::from(asset_id)), - amount, - pc, - is - } - ); - decoder.decode_type(ty_id, data); - } - fuel::Receipt::TransferOut { id, to, asset_id, amount, pc, is, .. } => { - #check_if_subscribed_to_contract - let ty_id = TransferOut::type_id(); - let data = serialize( - &TransferOut { - contract_id: ContractId::from(<[u8; 32]>::from(id)), - to: Address::from(<[u8; 32]>::from(to)), - asset_id: AssetId::from(<[u8; 32]>::from(asset_id)), - amount, - pc, - is - } - ); - decoder.decode_type(ty_id, data); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::ScriptResult { result, gas_used } => { + let ty_id = ScriptResult::type_id(); + let data = serialize(&ScriptResult{ result: u64::from(result), gas_used }); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::Transfer { id, to, asset_id, amount, pc, is, .. } => { + #check_if_subscribed_to_contract + let ty_id = Transfer::type_id(); + let data = serialize( + &Transfer { + contract_id: ContractId::from(<[u8; 32]>::from(id)), + to: ContractId::from(<[u8; 32]>::from(to)), + asset_id: AssetId::from(<[u8; 32]>::from(asset_id)), + amount, + pc, + is } - fuel::Receipt::Panic { id, reason, .. } => { - #check_if_subscribed_to_contract - let ty_id = Panic::type_id(); - let data = serialize( - &Panic { - contract_id: ContractId::from(<[u8; 32]>::from(id)), - reason: *reason.reason() as u32 - } - ); - decoder.decode_type(ty_id, data); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::TransferOut { id, to, asset_id, amount, pc, is, .. } => { + #check_if_subscribed_to_contract + let ty_id = TransferOut::type_id(); + let data = serialize( + &TransferOut { + contract_id: ContractId::from(<[u8; 32]>::from(id)), + to: Address::from(<[u8; 32]>::from(to)), + asset_id: AssetId::from(<[u8; 32]>::from(asset_id)), + amount, + pc, + is } - fuel::Receipt::Revert { id, ra, .. } => { - #check_if_subscribed_to_contract - let ty_id = Revert::type_id(); - let data = serialize( - &Revert { - contract_id: ContractId::from(<[u8; 32]>::from(id)), - error_val: u64::from(ra & 0xF) - } - ); - decoder.decode_type(ty_id, data); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::Panic { id, reason, .. } => { + #check_if_subscribed_to_contract + let ty_id = Panic::type_id(); + let data = serialize( + &Panic { + contract_id: ContractId::from(<[u8; 32]>::from(id)), + reason: *reason.reason() as u32 } - fuel::Receipt::Mint { sub_id, contract_id, val, pc, is } => { - let ty_id = Mint::type_id(); - let data = serialize( - &Mint { - sub_id: AssetId::from(<[u8; 32]>::from(sub_id)), - contract_id: ContractId::from(<[u8; 32]>::from(contract_id)), - val, - pc, - is - } - ); - decoder.decode_type(ty_id, data); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::Revert { id, ra, .. } => { + #check_if_subscribed_to_contract + let ty_id = Revert::type_id(); + let data = serialize( + &Revert { + contract_id: ContractId::from(<[u8; 32]>::from(id)), + error_val: u64::from(ra & 0xF) } - fuel::Receipt::Burn { sub_id, contract_id, val, pc, is } => { - let ty_id = Burn::type_id(); - let data = serialize( - &Burn { - sub_id: AssetId::from(<[u8; 32]>::from(sub_id)), - contract_id: ContractId::from(<[u8; 32]>::from(contract_id)), - val, - pc, - is - } - ); - decoder.decode_type(ty_id, data); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::Mint { sub_id, contract_id, val, pc, is } => { + let ty_id = Mint::type_id(); + let data = serialize( + &Mint { + sub_id: AssetId::from(<[u8; 32]>::from(sub_id)), + contract_id: ContractId::from(<[u8; 32]>::from(contract_id)), + val, + pc, + is } - _ => { - info!("This type is not handled yet. (>'.')>"); + ); + decoder.decode_type(ty_id, data); + } + fuel::Receipt::Burn { sub_id, contract_id, val, pc, is } => { + let ty_id = Burn::type_id(); + let data = serialize( + &Burn { + sub_id: AssetId::from(<[u8; 32]>::from(sub_id)), + contract_id: ContractId::from(<[u8; 32]>::from(contract_id)), + val, + pc, + is } - } + ); + decoder.decode_type(ty_id, data); + } + _ => { + info!("This type is not handled yet. (>'.')>"); } } + } + } - decoder.dispatch(block.header.height); + decoder.dispatch(block.header.height); - let metadata = IndexMetadataEntity::new(block.time as u64, block.header.height, block.id); - metadata.save(); - } - }, + let metadata = IndexMetadataEntity::new(block.time as u64, block.header.height, block.id); + metadata.save(); + } + }, quote! { #decoder_struct