diff --git a/artifacts/test_program_methods/group_pda_spender.bin b/artifacts/test_program_methods/group_pda_spender.bin new file mode 100644 index 00000000..16efb8a4 Binary files /dev/null and b/artifacts/test_program_methods/group_pda_spender.bin differ diff --git a/artifacts/test_program_methods/private_pda_claimer.bin b/artifacts/test_program_methods/private_pda_claimer.bin new file mode 100644 index 00000000..5a64c66d Binary files /dev/null and b/artifacts/test_program_methods/private_pda_claimer.bin differ diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml deleted file mode 100644 index 2137cb74..00000000 --- a/bedrock_client/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "bedrock_client" -version = "0.1.0" -edition = "2024" -license = { workspace = true } - -[lints] -workspace = true - -[dependencies] -common.workspace = true - -reqwest.workspace = true -anyhow.workspace = true -tokio-retry.workspace = true -futures.workspace = true -log.workspace = true -serde.workspace = true -humantime-serde.workspace = true -logos-blockchain-common-http-client.workspace = true -logos-blockchain-core.workspace = true -logos-blockchain-chain-broadcast-service.workspace = true -logos-blockchain-chain-service.workspace = true diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs deleted file mode 100644 index 4e9bfffd..00000000 --- a/bedrock_client/src/lib.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::time::Duration; - -use anyhow::{Context as _, Result}; -use common::config::BasicAuth; -use futures::{Stream, TryFutureExt as _}; -#[expect(clippy::single_component_path_imports, reason = "Satisfy machete")] -use humantime_serde; -use log::{info, warn}; -pub use logos_blockchain_chain_broadcast_service::BlockInfo; -use logos_blockchain_chain_service::CryptarchiaInfo; -pub use logos_blockchain_common_http_client::{CommonHttpClient, Error}; -pub use logos_blockchain_core::{block::Block, header::HeaderId, mantle::SignedMantleTx}; -use reqwest::{Client, Url}; -use serde::{Deserialize, Serialize}; -use tokio_retry::Retry; - -/// Fibonacci backoff retry strategy configuration. -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub struct BackoffConfig { - #[serde(with = "humantime_serde")] - pub start_delay: Duration, - pub max_retries: usize, -} - -impl Default for BackoffConfig { - fn default() -> Self { - Self { - start_delay: Duration::from_millis(100), - max_retries: 5, - } - } -} - -/// Simple wrapper -/// maybe extend in the future for our purposes -/// `Clone` is cheap because `CommonHttpClient` is internally reference counted (`Arc`). -#[derive(Clone)] -pub struct BedrockClient { - http_client: CommonHttpClient, - node_url: Url, - backoff: BackoffConfig, -} - -impl BedrockClient { - pub fn new(backoff: BackoffConfig, node_url: Url, auth: Option) -> Result { - info!("Creating Bedrock client with node URL {node_url}"); - let client = Client::builder() - //Add more fields if needed - .timeout(std::time::Duration::from_mins(1)) - .build() - .context("Failed to build HTTP client")?; - - let auth = auth.map(|a| { - logos_blockchain_common_http_client::BasicAuthCredentials::new(a.username, a.password) - }); - - let http_client = CommonHttpClient::new_with_client(client, auth); - Ok(Self { - http_client, - node_url, - backoff, - }) - } - - pub async fn post_transaction(&self, tx: SignedMantleTx) -> Result, Error> { - Retry::spawn(self.backoff_strategy(), || async { - match self - .http_client - .post_transaction(self.node_url.clone(), tx.clone()) - .await - { - Ok(()) => Ok(Ok(())), - Err(err) => match err { - // Retry arm. - // Retrying only reqwest errors: mainly connected to http. - Error::Request(_) => Err(err), - // Returning non-retryable error - Error::Server(_) | Error::Client(_) | Error::Url(_) => Ok(Err(err)), - }, - } - }) - .await - } - - pub async fn get_lib_stream(&self) -> Result, Error> { - self.http_client.get_lib_stream(self.node_url.clone()).await - } - - pub async fn get_block_by_id( - &self, - header_id: HeaderId, - ) -> Result>, Error> { - Retry::spawn(self.backoff_strategy(), || { - self.http_client - .get_block_by_id(self.node_url.clone(), header_id) - .inspect_err(|err| warn!("Block fetching failed with error: {err:#}")) - }) - .await - } - - pub async fn get_consensus_info(&self) -> Result { - Retry::spawn(self.backoff_strategy(), || { - self.http_client - .consensus_info(self.node_url.clone()) - .inspect_err(|err| warn!("Block fetching failed with error: {err:#}")) - }) - .await - } - - fn backoff_strategy(&self) -> impl Iterator { - let start_delay_millis = self - .backoff - .start_delay - .as_millis() - .try_into() - .expect("Start delay must be less than u64::MAX milliseconds"); - - tokio_retry::strategy::FibonacciBackoff::from_millis(start_delay_millis) - .take(self.backoff.max_retries) - } -} diff --git a/indexer_ffi/Cargo.toml b/indexer_ffi/Cargo.toml deleted file mode 100644 index b55230c6..00000000 --- a/indexer_ffi/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -edition = "2024" -license = { workspace = true } -name = "indexer_ffi" -version = "0.1.0" - -[dependencies] -indexer_service.workspace = true -log = { workspace = true } -tokio = { features = ["rt-multi-thread"], workspace = true } - -[build-dependencies] -cbindgen = "0.29" - -[lib] -crate-type = ["rlib", "cdylib", "staticlib"] -name = "indexer_ffi" - -[lints] -workspace = true - -[package.metadata.cargo-machete] -ignored = [ - "cbindgen", -] # machete does not recognize this for build dep and complains. diff --git a/indexer_ffi/build.rs b/indexer_ffi/build.rs deleted file mode 100644 index 92c95407..00000000 --- a/indexer_ffi/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::env; - -fn main() { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - println!("cargo:rerun-if-changed=src/"); - cbindgen::Builder::new() - .with_crate(crate_dir) - .with_language(cbindgen::Language::C) - .generate() - .expect("Unable to generate bindings") - .write_to_file("indexer_ffi.h"); -} diff --git a/indexer_ffi/cbindgen.toml b/indexer_ffi/cbindgen.toml deleted file mode 100644 index 79f622b7..00000000 --- a/indexer_ffi/cbindgen.toml +++ /dev/null @@ -1,2 +0,0 @@ -language = "C" # For increased compatibility -no_includes = true diff --git a/indexer_ffi/indexer_ffi.h b/indexer_ffi/indexer_ffi.h deleted file mode 100644 index 7c7d9a4d..00000000 --- a/indexer_ffi/indexer_ffi.h +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include -#include - -typedef enum OperationStatus { - Ok = 0, - NullPointer = 1, - InitializationError = 2, -} OperationStatus; - -typedef struct IndexerServiceFFI { - void *indexer_handle; - void *runtime; -} IndexerServiceFFI; - -/** - * Simple wrapper around a pointer to a value or an error. - * - * Pointer is not guaranteed. You should check the error field before - * dereferencing the pointer. - */ -typedef struct PointerResult_IndexerServiceFFI__OperationStatus { - struct IndexerServiceFFI *value; - enum OperationStatus error; -} PointerResult_IndexerServiceFFI__OperationStatus; - -typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult; - -/** - * Creates and starts an indexer based on the provided - * configuration file path. - * - * # Arguments - * - * - `config_path`: A pointer to a string representing the path to the configuration file. - * - `port`: Number representing a port, on which indexers RPC will start. - * - * # Returns - * - * An `InitializedIndexerServiceFFIResult` containing either a pointer to the - * initialized `IndexerServiceFFI` or an error code. - */ -InitializedIndexerServiceFFIResult start_indexer(const char *config_path, uint16_t port); - -/** - * Stops and frees the resources associated with the given indexer service. - * - * # Arguments - * - * - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped. - * - * # Returns - * - * An `OperationStatus` indicating success or failure. - * - * # Safety - * - * The caller must ensure that: - * - `indexer` is a valid pointer to a `IndexerServiceFFI` instance - * - The `IndexerServiceFFI` instance was created by this library - * - The pointer will not be used after this function returns - */ -enum OperationStatus stop_indexer(struct IndexerServiceFFI *indexer); - -/** - * # Safety - * It's up to the caller to pass a proper pointer, if somehow from c/c++ side - * this is called with a type which doesn't come from a returned `CString` it - * will cause a segfault. - */ -void free_cstring(char *block); - -bool is_ok(const enum OperationStatus *self); - -bool is_error(const enum OperationStatus *self); diff --git a/indexer_ffi/src/api/lifecycle.rs b/indexer_ffi/src/api/lifecycle.rs deleted file mode 100644 index 735efd4d..00000000 --- a/indexer_ffi/src/api/lifecycle.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::{ffi::c_char, path::PathBuf}; - -use tokio::runtime::Runtime; - -use crate::{IndexerServiceFFI, api::PointerResult, errors::OperationStatus}; - -pub type InitializedIndexerServiceFFIResult = PointerResult; - -/// Creates and starts an indexer based on the provided -/// configuration file path. -/// -/// # Arguments -/// -/// - `config_path`: A pointer to a string representing the path to the configuration file. -/// - `port`: Number representing a port, on which indexers RPC will start. -/// -/// # Returns -/// -/// An `InitializedIndexerServiceFFIResult` containing either a pointer to the -/// initialized `IndexerServiceFFI` or an error code. -#[unsafe(no_mangle)] -pub extern "C" fn start_indexer( - config_path: *const c_char, - port: u16, -) -> InitializedIndexerServiceFFIResult { - setup_indexer(config_path, port).map_or_else( - InitializedIndexerServiceFFIResult::from_error, - InitializedIndexerServiceFFIResult::from_value, - ) -} - -/// Initializes and starts an indexer based on the provided -/// configuration file path. -/// -/// # Arguments -/// -/// - `config_path`: A pointer to a string representing the path to the configuration file. -/// - `port`: Number representing a port, on which indexers RPC will start. -/// -/// # Returns -/// -/// A `Result` containing either the initialized `IndexerServiceFFI` or an -/// error code. -fn setup_indexer( - config_path: *const c_char, - port: u16, -) -> Result { - let user_config_path = PathBuf::from( - unsafe { std::ffi::CStr::from_ptr(config_path) } - .to_str() - .map_err(|e| { - log::error!("Could not convert the config path to string: {e}"); - OperationStatus::InitializationError - })?, - ); - let config = indexer_service::IndexerConfig::from_path(&user_config_path).map_err(|e| { - log::error!("Failed to read config: {e}"); - OperationStatus::InitializationError - })?; - - let rt = Runtime::new().unwrap(); - - let indexer_handle = rt - .block_on(indexer_service::run_server(config, port)) - .map_err(|e| { - log::error!("Could not start indexer service: {e}"); - OperationStatus::InitializationError - })?; - - Ok(IndexerServiceFFI::new(indexer_handle, rt)) -} - -/// Stops and frees the resources associated with the given indexer service. -/// -/// # Arguments -/// -/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped. -/// -/// # Returns -/// -/// An `OperationStatus` indicating success or failure. -/// -/// # Safety -/// -/// The caller must ensure that: -/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance -/// - The `IndexerServiceFFI` instance was created by this library -/// - The pointer will not be used after this function returns -#[unsafe(no_mangle)] -pub unsafe extern "C" fn stop_indexer(indexer: *mut IndexerServiceFFI) -> OperationStatus { - if indexer.is_null() { - log::error!("Attempted to stop a null indexer pointer. This is a bug. Aborting."); - return OperationStatus::NullPointer; - } - - let indexer = unsafe { Box::from_raw(indexer) }; - drop(indexer); - - OperationStatus::Ok -} diff --git a/indexer_ffi/src/api/memory.rs b/indexer_ffi/src/api/memory.rs deleted file mode 100644 index f266d309..00000000 --- a/indexer_ffi/src/api/memory.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::ffi::{CString, c_char}; - -/// # Safety -/// It's up to the caller to pass a proper pointer, if somehow from c/c++ side -/// this is called with a type which doesn't come from a returned `CString` it -/// will cause a segfault. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn free_cstring(block: *mut c_char) { - if block.is_null() { - log::error!("Trying to free a null pointer. Exiting"); - return; - } - drop(unsafe { CString::from_raw(block) }); -} diff --git a/indexer_ffi/src/api/mod.rs b/indexer_ffi/src/api/mod.rs deleted file mode 100644 index e84a3913..00000000 --- a/indexer_ffi/src/api/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use result::PointerResult; - -pub mod lifecycle; -pub mod memory; -pub mod result; diff --git a/indexer_ffi/src/api/result.rs b/indexer_ffi/src/api/result.rs deleted file mode 100644 index 96cbcdd8..00000000 --- a/indexer_ffi/src/api/result.rs +++ /dev/null @@ -1,29 +0,0 @@ -/// Simple wrapper around a pointer to a value or an error. -/// -/// Pointer is not guaranteed. You should check the error field before -/// dereferencing the pointer. -#[repr(C)] -pub struct PointerResult { - pub value: *mut Type, - pub error: Error, -} - -impl PointerResult { - pub fn from_pointer(pointer: *mut Type) -> Self { - Self { - value: pointer, - error: Error::default(), - } - } - - pub fn from_value(value: Type) -> Self { - Self::from_pointer(Box::into_raw(Box::new(value))) - } - - pub const fn from_error(error: Error) -> Self { - Self { - value: std::ptr::null_mut(), - error, - } - } -} diff --git a/indexer_ffi/src/errors.rs b/indexer_ffi/src/errors.rs deleted file mode 100644 index 46aa0f9f..00000000 --- a/indexer_ffi/src/errors.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[derive(Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub enum OperationStatus { - #[default] - Ok = 0x0, - NullPointer = 0x1, - InitializationError = 0x2, -} - -impl OperationStatus { - #[must_use] - #[unsafe(no_mangle)] - pub extern "C" fn is_ok(&self) -> bool { - *self == Self::Ok - } - - #[must_use] - #[unsafe(no_mangle)] - pub extern "C" fn is_error(&self) -> bool { - !self.is_ok() - } -} diff --git a/indexer_ffi/src/indexer.rs b/indexer_ffi/src/indexer.rs deleted file mode 100644 index c110b183..00000000 --- a/indexer_ffi/src/indexer.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::{ffi::c_void, net::SocketAddr}; - -use indexer_service::IndexerHandle; -use tokio::runtime::Runtime; - -#[repr(C)] -pub struct IndexerServiceFFI { - indexer_handle: *mut c_void, - runtime: *mut c_void, -} - -impl IndexerServiceFFI { - pub fn new(indexer_handle: indexer_service::IndexerHandle, runtime: Runtime) -> Self { - Self { - // Box the complex types and convert to opaque pointers - indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::(), - runtime: Box::into_raw(Box::new(runtime)).cast::(), - } - } - - /// Helper to take ownership back. - /// - /// # Safety - /// - /// The caller must ensure that: - /// - `self` is a valid object(contains valid pointers in all fields) - #[must_use] - pub unsafe fn into_parts(self) -> (Box, Box) { - let indexer_handle = unsafe { Box::from_raw(self.indexer_handle.cast::()) }; - let runtime = unsafe { Box::from_raw(self.runtime.cast::()) }; - (indexer_handle, runtime) - } - - /// Helper to get indexer handle addr. - /// - /// # Safety - /// - /// The caller must ensure that: - /// - `self` is a valid object(contains valid pointers in all fields) - #[must_use] - pub const unsafe fn addr(&self) -> SocketAddr { - let indexer_handle = unsafe { - self.indexer_handle - .cast::() - .as_ref() - .expect("Indexer Handle must be non-null pointer") - }; - - indexer_handle.addr() - } - - /// Helper to get indexer handle addr. - /// - /// # Safety - /// - /// The caller must ensure that: - /// - `self` is a valid object(contains valid pointers in all fields) - #[must_use] - pub const unsafe fn handle(&self) -> &IndexerHandle { - unsafe { - self.indexer_handle - .cast::() - .as_ref() - .expect("Indexer Handle must be non-null pointer") - } - } -} - -// Implement Drop to prevent memory leaks -impl Drop for IndexerServiceFFI { - fn drop(&mut self) { - let Self { - indexer_handle, - runtime, - } = self; - - if indexer_handle.is_null() { - log::error!("Attempted to drop a null indexer pointer. This is a bug"); - } - if runtime.is_null() { - log::error!("Attempted to drop a null tokio runtime pointer. This is a bug"); - } - drop(unsafe { Box::from_raw(indexer_handle.cast::()) }); - drop(unsafe { Box::from_raw(runtime.cast::()) }); - } -} diff --git a/indexer_ffi/src/lib.rs b/indexer_ffi/src/lib.rs deleted file mode 100644 index fe594ec0..00000000 --- a/indexer_ffi/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![allow(clippy::undocumented_unsafe_blocks, reason = "It is an FFI")] - -pub use errors::OperationStatus; -pub use indexer::IndexerServiceFFI; - -pub mod api; -mod errors; -mod indexer;