Skip to content

Commit

Permalink
fix: update build scripts since we need lumina-node types
Browse files Browse the repository at this point in the history
  • Loading branch information
sebasti810 committed Dec 17, 2024
1 parent 04093b8 commit 8a858d8
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 183 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 node-uniffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ libp2p.workspace = true
redb = "2.1.1"
thiserror = "1.0.61"
serde_json = "1.0.64"
directories = "5.0.1"
uniffi = { version = "0.28.3", features = ["bindgen", "tokio", "cli"] }
tokio = { version = "1.38.0", features = ["macros", "sync"] }
6 changes: 4 additions & 2 deletions node-uniffi/build-android.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

cargo build -p native
cargo build -p lumina-node-uniffi

rustup target add \
aarch64-linux-android \
Expand All @@ -16,4 +16,6 @@ cargo ndk -o ./app/src/main/jniLibs \
-t x86_64 \
build --release

cargo run --bin uniffi-bindgen generate --library ../target/debug/libnative.dylib --language kotlin --out-dir ./app/src/main/java/tech/forgen/native/rust
cargo run --bin uniffi-bindgen generate --library ../target/debug/liblumina_node_uniffi.dylib --language kotlin --out-dir ./app/src/main/java/tech/forgen/lumina_node_uniffi/rust

echo "Android build complete"
51 changes: 28 additions & 23 deletions node-uniffi/build-ios.sh
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
#!/bin/bash

cargo build -p native

mkdir -p ./bindings
mkdir -p ./ios

cargo run --bin uniffi-bindgen generate --library ../target/debug/libnative.dylib --language swift --out-dir ./bindings

cd ..

for TARGET in \
aarch64-apple-darwin \
aarch64-apple-ios \
aarch64-apple-ios-sim \
x86_64-apple-darwin \
x86_64-apple-ios
aarch64-apple-ios-sim
do
rustup target add $TARGET
cargo build --release --target=$TARGET
done

mv ./bindings/nativeFFI.modulemap ./bindings/module.modulemap

rm ./ios/Native.swift
mv ./bindings/native.swift ./ios/Native.swift

rm -rf "ios/Native.xcframework"

cd node-uniffi

rm -rf ./bindings ./ios
mkdir -p ./bindings
mkdir -p ./ios
mkdir -p ./bindings/Headers

cargo run --bin uniffi-bindgen generate --library ../target/debug/liblumina_node_uniffi.dylib --language swift --out-dir ./bindings

cat "./bindings/lumina_node_uniffiFFI.modulemap" "./bindings/lumina_nodeFFI.modulemap" > "./bindings/Headers/module.modulemap"

cp ./bindings/*.h ./bindings/Headers/

rm -rf "ios/lumina.xcframework"

xcodebuild -create-xcframework \
-library ../target/aarch64-apple-ios-sim/release/libnative.a -headers ./bindings \
-library ../target/aarch64-apple-ios/release/libnative.a -headers ./bindings \
-output "ios/Native.xcframework"

rm -rf bindings
-library ../target/aarch64-apple-ios-sim/release/liblumina_node_uniffi.a -headers ./bindings/Headers \
-library ../target/aarch64-apple-ios/release/liblumina_node_uniffi.a -headers ./bindings/Headers \
-output "ios/lumina.xcframework"

cp ./bindings/*.swift ./ios/

rm -rf bindings

echo "iOS build complete"
72 changes: 72 additions & 0 deletions node-uniffi/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use lumina_node::NodeError;
use thiserror::Error;

/// Result type alias for LuminaNode operations that can fail with a LuminaError
pub type Result<T> = std::result::Result<T, LuminaError>;

/// Represents all possible errors that can occur in the LuminaNode.
#[derive(Error, Debug, uniffi::Error)]
pub enum LuminaError {
/// Error returned when trying to perform operations on a node that isn't running
#[error("Node is not running")]
NodeNotRunning,

/// Error returned when network operations fail
#[error("Network error: {msg}")]
NetworkError {
/// Description of the network error
msg: String,
},

/// Error returned when storage operations fail
#[error("Storage error: {msg}")]
StorageError {
/// Description of the storage error
msg: String,
},

/// Error returned when trying to start a node that's already running
#[error("Node is already running")]
AlreadyRunning,

/// Error returned when a mutex lock operation fails
#[error("Lock error")]
LockError,

/// Error returned when a hash string is invalid or malformed
#[error("Invalid hash format: {msg}")]
InvalidHash {
/// Description of why the hash is invalid
msg: String,
},

/// Error returned when a header is invalid or malformed
#[error("Invalid header format: {msg}")]
InvalidHeader {
/// Description of why the header is invalid
msg: String,
},

/// Error returned when storage initialization fails
#[error("Storage initialization failed: {msg}")]
StorageInit {
/// Description of why storage initialization failed
msg: String,
},
}

impl From<NodeError> for LuminaError {
fn from(error: NodeError) -> Self {
LuminaError::NetworkError {
msg: error.to_string(),
}
}
}

impl From<libp2p::multiaddr::Error> for LuminaError {
fn from(e: libp2p::multiaddr::Error) -> Self {
LuminaError::NetworkError {
msg: format!("Invalid multiaddr: {}", e),
}
}
}
159 changes: 23 additions & 136 deletions node-uniffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,125 +4,29 @@
//! allowing it to be used from iOS and Android applications.
#![cfg(not(target_arch = "wasm32"))]

mod error;
mod types;

use celestia_types::ExtendedHeader;
use libp2p::identity::Keypair;
use error::{LuminaError, Result};
use lumina_node::{
blockstore::RedbBlockstore,
events::{EventSubscriber, TryRecvError},
network::Network,
node::PeerTrackerInfo,
store::RedbStore,
Node, NodeError,
Node,
};
use std::{path::PathBuf, str::FromStr, sync::Arc};
use std::{str::FromStr, sync::Arc};
use tendermint::hash::Hash;
use thiserror::Error;
use tokio::sync::Mutex;
use types::{NetworkInfo, NodeEvent, PeerId, SyncingInfo};
use types::{NetworkInfo, NodeEvent, NodeStartConfig, PeerId, SyncingInfo};
use uniffi::Object;

uniffi::setup_scaffolding!();

lumina_node::uniffi_reexport_scaffolding!();

/// Result type alias for LuminaNode operations that can fail with a LuminaError
pub type Result<T> = std::result::Result<T, LuminaError>;

/// Returns the platform-specific base path for storing Lumina data.
///
/// The function determines the base path based on the target operating system:
/// - **iOS**: `~/Library/Application Support/lumina`
/// - **Android**: Value of the `LUMINA_DATA_DIR` environment variable
/// - **Other platforms**: Returns an error indicating unsupported platform.
fn get_base_path() -> Result<PathBuf> {
#[cfg(target_os = "ios")]
{
std::env::var("HOME")
.map(PathBuf::from)
.map(|p| p.join("Library/Application Support/lumina"))
.map_err(|e| LuminaError::StorageError {
msg: format!("Could not get HOME directory: {}", e),
})
}

#[cfg(target_os = "android")]
{
std::env::var("LUMINA_DATA_DIR")
.map(PathBuf::from)
.map_err(|e| LuminaError::StorageError {
msg: format!("Could not get LUMINA_DATA_DIR: {}", e),
})
}

#[cfg(not(any(target_os = "ios", target_os = "android")))]
{
Err(LuminaError::StorageError {
msg: "Unsupported platform".to_string(),
})
}
}

/// Represents all possible errors that can occur in the LuminaNode.
#[derive(Error, Debug, uniffi::Error)]
pub enum LuminaError {
/// Error returned when trying to perform operations on a node that isn't running
#[error("Node is not running")]
NodeNotRunning,

/// Error returned when network operations fail
#[error("Network error: {msg}")]
NetworkError {
/// Description of the network error
msg: String,
},

/// Error returned when storage operations fail
#[error("Storage error: {msg}")]
StorageError {
/// Description of the storage error
msg: String,
},

/// Error returned when trying to start a node that's already running
#[error("Node is already running")]
AlreadyRunning,

/// Error returned when a mutex lock operation fails
#[error("Lock error")]
LockError,

/// Error returned when a hash string is invalid or malformed
#[error("Invalid hash format: {msg}")]
InvalidHash {
/// Description of why the hash is invalid
msg: String,
},

/// Error returned when a header is invalid or malformed
#[error("Invalid header format: {msg}")]
InvalidHeader {
/// Description of why the header is invalid
msg: String,
},

/// Error returned when storage initialization fails
#[error("Storage initialization failed: {msg}")]
StorageInit {
/// Description of why storage initialization failed
msg: String,
},
}

impl From<NodeError> for LuminaError {
fn from(error: NodeError) -> Self {
LuminaError::NetworkError {
msg: error.to_string(),
}
}
}

/// The main Lumina node that manages the connection to the Celestia network.
#[derive(Object)]
pub struct LuminaNode {
Expand All @@ -143,56 +47,39 @@ impl LuminaNode {
}))
}

/// Starts the Lumina node. Returns true if successfully started.
/// Start the node without optional configuration.
/// UniFFI needs explicit handling for optional parameters to generate correct bindings for different languages.
pub async fn start(&self) -> Result<bool> {
self.start_with_config(None).await
}

/// Start the node with specific configuration
pub async fn start_with_config(&self, config: Option<NodeStartConfig>) -> Result<bool> {
let mut node_guard = self.node.lock().await;

if node_guard.is_some() {
return Err(LuminaError::AlreadyRunning);
}

let network_id = self.network.id();

let base_path = get_base_path()?;

std::fs::create_dir_all(&base_path).map_err(|e| LuminaError::StorageError {
msg: format!("Failed to create data directory: {}", e),
})?;
let config = config.unwrap_or_else(|| NodeStartConfig {
network: self.network.clone(),
bootnodes: None,
syncing_window_secs: None,
pruning_delay_secs: None,
batch_size: None,
ed25519_secret_key_bytes: None,
});

let store_path = base_path.join(format!("store-{}", network_id));
let db = Arc::new(redb::Database::create(&store_path).map_err(|e| {
LuminaError::StorageInit {
msg: format!("Failed to create database: {}", e),
}
})?);

let store = RedbStore::new(db.clone())
.await
.map_err(|e| LuminaError::StorageInit {
msg: format!("Failed to initialize store: {}", e),
})?;

let blockstore = RedbBlockstore::new(db);

let p2p_bootnodes = self.network.canonical_bootnodes().collect::<Vec<_>>();
let p2p_local_keypair = Keypair::generate_ed25519();

let builder = Node::builder()
.store(store)
.blockstore(blockstore)
.network(self.network.clone())
.bootnodes(p2p_bootnodes)
.keypair(p2p_local_keypair)
.sync_batch_size(128);
let builder = config.into_node_builder().await?;

let (new_node, subscriber) = builder
.start_subscribed()
.await
.map_err(|e| LuminaError::NetworkError { msg: e.to_string() })?;

let mut events_guard = self.events_subscriber.lock().await;
*events_guard = Some(subscriber);

*events_guard = Some(subscriber);
*node_guard = Some(new_node);
Ok(true)
}
Expand Down Expand Up @@ -268,7 +155,7 @@ impl LuminaNode {
let node = node_guard.as_ref().ok_or(LuminaError::NodeNotRunning)?;
let peer_id = peer_id
.to_libp2p()
.map_err(|e| LuminaError::NetworkError { msg: e })?;
.map_err(|e| LuminaError::NetworkError { msg: e.to_string() })?;
Ok(node.set_peer_trust(peer_id, is_trusted).await?)
}

Expand Down
Loading

0 comments on commit 8a858d8

Please sign in to comment.