mirror of
https://github.com/logos-blockchain/logos-blockchain.git
synced 2026-01-02 05:03:10 +00:00
feat(c-bindings): Wallet API (#1960)
This commit is contained in:
parent
3f623e0c9d
commit
22ee405d18
62
Cargo.lock
generated
62
Cargo.lock
generated
@ -85,9 +85,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923"
|
||||
checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
|
||||
dependencies = [
|
||||
"as-slice",
|
||||
]
|
||||
@ -1709,7 +1709,7 @@ dependencies = [
|
||||
"nomos-tracing-service",
|
||||
"nomos-utils",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
@ -1994,7 +1994,7 @@ dependencies = [
|
||||
"nomos-core",
|
||||
"nomos-da-messages",
|
||||
"nomos-http-api-common",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
@ -3182,7 +3182,7 @@ dependencies = [
|
||||
"futures",
|
||||
"nomos-core",
|
||||
"nomos-http-api-common",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -4346,7 +4346,7 @@ dependencies = [
|
||||
"rgb",
|
||||
"tiff",
|
||||
"zune-core 0.5.0",
|
||||
"zune-jpeg 0.5.6",
|
||||
"zune-jpeg 0.5.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5283,13 +5283,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5603,9 +5603,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "moxcms"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80986bbbcf925ebd3be54c26613d861255284584501595cf418320c078945608"
|
||||
checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"pxfm",
|
||||
@ -5929,7 +5929,7 @@ dependencies = [
|
||||
"nomos-sdp",
|
||||
"nomos-storage",
|
||||
"overwatch",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"subnetworks-assignations",
|
||||
@ -6101,7 +6101,14 @@ name = "nomos-c"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
"chain-service",
|
||||
"cryptarchia-engine",
|
||||
"key-management-system-keys",
|
||||
"nomos-api",
|
||||
"nomos-core",
|
||||
"nomos-node",
|
||||
"nomos-wallet",
|
||||
"num-bigint",
|
||||
"overwatch",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
@ -6117,7 +6124,7 @@ dependencies = [
|
||||
"kzgrs-backend",
|
||||
"nomos-core",
|
||||
"nomos-tracing",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -6587,7 +6594,7 @@ dependencies = [
|
||||
"opentelemetry-semantic-conventions",
|
||||
"opentelemetry_sdk",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -6979,7 +6986,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"http 1.4.0",
|
||||
"opentelemetry",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7167,7 +7174,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.5.18",
|
||||
"smallvec",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
@ -8334,6 +8341,15 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
@ -8461,9 +8477,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.25"
|
||||
version = "0.12.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a"
|
||||
checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@ -10057,7 +10073,7 @@ dependencies = [
|
||||
"nomos-wallet",
|
||||
"num-bigint",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"serial_test",
|
||||
@ -10604,7 +10620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3beec919fbdf99d719de8eda6adae3281f8a5b71ae40431f44dc7423053d34"
|
||||
dependencies = [
|
||||
"loki-api",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snap",
|
||||
@ -11040,7 +11056,7 @@ dependencies = [
|
||||
"axum",
|
||||
"mime_guess",
|
||||
"regex",
|
||||
"reqwest 0.12.25",
|
||||
"reqwest 0.12.26",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -12297,9 +12313,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.5.6"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f520eebad972262a1dde0ec455bce4f8b298b1e5154513de58c114c4c54303e8"
|
||||
checksum = "51d915729b0e7d5fe35c2f294c5dc10b30207cc637920e5b59077bfa3da63f28"
|
||||
dependencies = [
|
||||
"zune-core 0.5.0",
|
||||
]
|
||||
|
||||
@ -10,10 +10,17 @@ repository = { workspace = true }
|
||||
version = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
nomos-node = { default-features = true, workspace = true }
|
||||
overwatch = { workspace = true }
|
||||
serde_yaml = { default-features = false, workspace = true }
|
||||
tokio = { default-features = false, features = ["rt-multi-thread"], workspace = true }
|
||||
chain-service = { workspace = true }
|
||||
cryptarchia-engine = { workspace = true }
|
||||
key-management-system-keys = { workspace = true }
|
||||
nomos-api = { workspace = true }
|
||||
nomos-core = { workspace = true }
|
||||
nomos-node = { default-features = true, workspace = true }
|
||||
nomos-wallet = { workspace = true }
|
||||
num-bigint = { version = "0.4", default-features = false }
|
||||
overwatch = { workspace = true }
|
||||
serde_yaml = { default-features = false, workspace = true }
|
||||
tokio = { default-features = false, features = ["rt-multi-thread"], workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.29"
|
||||
|
||||
125
nomos-c/src/api/cryptarchia.rs
Normal file
125
nomos-c/src/api/cryptarchia.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::{
|
||||
NomosNode,
|
||||
api::{PointerResult, free},
|
||||
errors::OperationStatus,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub enum State {
|
||||
Bootstrapping = 0x0,
|
||||
Online = 0x1,
|
||||
}
|
||||
|
||||
impl From<cryptarchia_engine::State> for State {
|
||||
fn from(value: cryptarchia_engine::State) -> Self {
|
||||
match value {
|
||||
cryptarchia_engine::State::Bootstrapping => Self::Bootstrapping,
|
||||
cryptarchia_engine::State::Online => Self::Online,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Hash = [u8; 32];
|
||||
pub type HeaderId = Hash;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CryptarchiaInfo {
|
||||
pub lib: HeaderId,
|
||||
pub tip: HeaderId,
|
||||
pub slot: u64,
|
||||
pub height: u64,
|
||||
pub mode: State,
|
||||
}
|
||||
|
||||
impl From<chain_service::CryptarchiaInfo> for CryptarchiaInfo {
|
||||
fn from(value: chain_service::CryptarchiaInfo) -> Self {
|
||||
Self {
|
||||
lib: value.lib.into(),
|
||||
tip: value.tip.into(),
|
||||
slot: u64::from(value.slot),
|
||||
height: value.height,
|
||||
mode: State::from(value.mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the current Cryptarchia info.
|
||||
///
|
||||
/// This is a synchronous wrapper around the asynchronous
|
||||
/// [`cryptarchia_info`](nomos_api::http::consensus::cryptarchia_info) function.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A [`NomosNode`] instance.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` containing the [`CryptarchiaInfo`] on success, or an
|
||||
/// [`OperationStatus`] error on failure.
|
||||
pub(crate) fn get_cryptarchia_info_sync(
|
||||
node: &NomosNode,
|
||||
) -> Result<chain_service::CryptarchiaInfo, OperationStatus> {
|
||||
let Ok(runtime) = tokio::runtime::Runtime::new() else {
|
||||
eprintln!("[get_cryptarchia_info_sync] Failed to create tokio runtime. Aborting.");
|
||||
return Err(OperationStatus::RuntimeError);
|
||||
};
|
||||
let Ok(cryptarchia_info) = runtime.block_on(nomos_api::http::consensus::cryptarchia_info(
|
||||
node.get_overwatch_handle(),
|
||||
)) else {
|
||||
eprintln!("[get_cryptarchia_info_sync] Failed to get cryptarchia info. Aborting.");
|
||||
return Err(OperationStatus::RelayError);
|
||||
};
|
||||
Ok(cryptarchia_info)
|
||||
}
|
||||
|
||||
pub type CryptarchiaInfoResult = PointerResult<CryptarchiaInfo, OperationStatus>;
|
||||
|
||||
/// Get the current Cryptarchia info.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A non-null pointer to a [`NomosNode`].
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A [`CryptarchiaInfoResult`] containing a pointer to the allocated
|
||||
/// [`CryptarchiaInfo`] struct on success, or an [`OperationStatus`] error on
|
||||
/// failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it dereferences raw pointers.
|
||||
/// The caller must ensure that all pointers are non-null and point to valid
|
||||
/// memory.
|
||||
///
|
||||
/// # Memory Management
|
||||
///
|
||||
/// This function allocates memory for the output [`CryptarchiaInfo`] struct.
|
||||
/// The caller must free this memory using the [`free_cryptarchia_info`]
|
||||
/// function.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn get_cryptarchia_info(node: *const NomosNode) -> CryptarchiaInfoResult {
|
||||
if node.is_null() {
|
||||
eprintln!("[get_cryptarchia_info] Received a null `node` pointer. Exiting.");
|
||||
return CryptarchiaInfoResult::from_error(OperationStatus::NullPtr);
|
||||
}
|
||||
|
||||
let node = unsafe { &*node };
|
||||
match get_cryptarchia_info_sync(node) {
|
||||
Ok(cryptarchia_info) => {
|
||||
let cryptarchia_info = CryptarchiaInfo::from(cryptarchia_info);
|
||||
CryptarchiaInfoResult::from_value(cryptarchia_info)
|
||||
}
|
||||
Err(error) => CryptarchiaInfoResult::from_error(error),
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the memory allocated for a [`CryptarchiaInfo`] struct.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `pointer`: A pointer to the [`CryptarchiaInfo`] struct to be freed.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn free_cryptarchia_info(pointer: *mut CryptarchiaInfo) {
|
||||
free::<CryptarchiaInfo>(pointer);
|
||||
}
|
||||
@ -3,31 +3,40 @@ use std::ffi::c_char;
|
||||
use nomos_node::{Config, get_services_to_start, run_node_from_config};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::{NomosNode, errors::NomosNodeErrorCode};
|
||||
use crate::{NomosNode, api::PointerResult, errors::NomosNodeErrorCode};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct InitializedNomosNodeResult {
|
||||
nomos_node: *mut NomosNode,
|
||||
error_code: NomosNodeErrorCode,
|
||||
}
|
||||
pub type InitializedNomosNodeResult = PointerResult<NomosNode, NomosNodeErrorCode>;
|
||||
|
||||
/// Creates and starts a Nomos node based on the provided configuration file
|
||||
/// path.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `config_path`: A pointer to a string representing the path to the
|
||||
/// configuration file.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An `InitializedNomosNodeResult` containing either a pointer to the
|
||||
/// initialized `NomosNode` or an error code.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn start_nomos_node(config_path: *const c_char) -> InitializedNomosNodeResult {
|
||||
match initialize_nomos_node(config_path) {
|
||||
Ok(nomos_node) => {
|
||||
let node_ptr = Box::into_raw(Box::new(nomos_node));
|
||||
InitializedNomosNodeResult {
|
||||
nomos_node: node_ptr,
|
||||
error_code: NomosNodeErrorCode::None,
|
||||
}
|
||||
}
|
||||
Err(error_code) => InitializedNomosNodeResult {
|
||||
nomos_node: core::ptr::null_mut(),
|
||||
error_code,
|
||||
},
|
||||
}
|
||||
initialize_nomos_node(config_path).map_or_else(
|
||||
InitializedNomosNodeResult::from_error,
|
||||
InitializedNomosNodeResult::from_value,
|
||||
)
|
||||
}
|
||||
|
||||
/// Initializes and starts a Nomos node based on the provided configuration file
|
||||
/// path.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `config_path`: A pointer to a string representing the path to the
|
||||
/// configuration file.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` containing either the initialized `NomosNode` or an error code.
|
||||
fn initialize_nomos_node(config_path: *const c_char) -> Result<NomosNode, NomosNodeErrorCode> {
|
||||
// TODO: Remove flags when dynamic run of services is implemented.
|
||||
let must_blend_service_group_start = true;
|
||||
@ -79,13 +88,23 @@ fn initialize_nomos_node(config_path: *const c_char) -> Result<NomosNode, NomosN
|
||||
Ok(NomosNode::new(app, rt))
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
/// Stops and frees the resources associated with the given Nomos node.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A pointer to the `NomosNode` instance to be stopped.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An `NomosNodeErrorCode` indicating success or failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that:
|
||||
/// - `node` is a valid pointer to a `NomosNode` instance
|
||||
/// - The `NomosNode` 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_node(node: *mut NomosNode) -> NomosNodeErrorCode {
|
||||
if node.is_null() {
|
||||
eprintln!("Attempted to stop a null node pointer. This is a bug. Aborting.");
|
||||
|
||||
12
nomos-c/src/api/memory.rs
Normal file
12
nomos-c/src/api/memory.rs
Normal file
@ -0,0 +1,12 @@
|
||||
/// Frees memory allocated for a given pointer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `pointer` - A pointer to the memory to be freed.
|
||||
pub fn free<Type>(pointer: *mut Type) {
|
||||
if !pointer.is_null() {
|
||||
unsafe {
|
||||
drop(Box::from_raw(pointer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1 +1,8 @@
|
||||
pub mod cryptarchia;
|
||||
pub mod lifecycle;
|
||||
pub(crate) mod memory;
|
||||
pub(crate) mod result;
|
||||
pub mod wallet;
|
||||
|
||||
pub(crate) use memory::free;
|
||||
pub(crate) use result::{PointerResult, ValueResult};
|
||||
|
||||
55
nomos-c/src/api/result.rs
Normal file
55
nomos-c/src/api/result.rs
Normal file
@ -0,0 +1,55 @@
|
||||
/// Simple wrapper around a value or an error.
|
||||
///
|
||||
/// Value is not guaranteed. You should check the error field before accessing
|
||||
/// the value.
|
||||
#[repr(C)]
|
||||
pub struct ValueResult<Type, Error> {
|
||||
pub value: Type,
|
||||
pub error: Error,
|
||||
}
|
||||
|
||||
impl<Type: Default, Error: Default> ValueResult<Type, Error> {
|
||||
pub fn from_value(value: Type) -> Self {
|
||||
Self {
|
||||
value,
|
||||
error: Error::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_error(error: Error) -> Self {
|
||||
Self {
|
||||
value: Type::default(),
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Type, Error> {
|
||||
pub value: *mut Type,
|
||||
pub error: Error,
|
||||
}
|
||||
|
||||
impl<Type, Error: Default> PointerResult<Type, Error> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
326
nomos-c/src/api/wallet.rs
Normal file
326
nomos-c/src/api/wallet.rs
Normal file
@ -0,0 +1,326 @@
|
||||
use key_management_system_keys::keys::ZkPublicKey;
|
||||
use nomos_core::mantle::{SignedMantleTx, Transaction as _, Value};
|
||||
use nomos_wallet::{WalletService, api::WalletApi};
|
||||
use num_bigint::BigUint;
|
||||
|
||||
use crate::{
|
||||
NomosNode,
|
||||
api::{
|
||||
ValueResult,
|
||||
cryptarchia::{Hash, HeaderId, get_cryptarchia_info_sync},
|
||||
free,
|
||||
},
|
||||
errors::OperationStatus,
|
||||
};
|
||||
|
||||
/// Get the balance of a wallet address
|
||||
///
|
||||
/// This is a synchronous wrapper around [`WalletApi::get_balance`].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A [`NomosNode`] instance.
|
||||
/// - `tip`: The header ID to query the balance at.
|
||||
/// - `wallet_address`: The public key of the wallet address to query.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` containing an [`Option<Value>`] on success, or an
|
||||
/// [`OperationStatus`] error on failure.
|
||||
pub(crate) fn get_balance_sync(
|
||||
node: &NomosNode,
|
||||
tip: nomos_core::header::HeaderId,
|
||||
wallet_address: ZkPublicKey,
|
||||
) -> Result<Option<Value>, OperationStatus> {
|
||||
let Ok(runtime) = tokio::runtime::Runtime::new() else {
|
||||
eprintln!("[Failed]to create tokio runtime. Aborting.");
|
||||
return Err(OperationStatus::RuntimeError);
|
||||
};
|
||||
|
||||
runtime
|
||||
.block_on(async {
|
||||
let api = WalletApi::<WalletService<_, _, _, _, _>, _>::from_overwatch_handle(
|
||||
node.get_overwatch_handle(),
|
||||
)
|
||||
.await;
|
||||
api.get_balance(tip, wallet_address).await
|
||||
})
|
||||
.map_err(|_| OperationStatus::DynError)
|
||||
}
|
||||
|
||||
pub type BalanceResult = ValueResult<Value, OperationStatus>;
|
||||
|
||||
/// Get the balance of a wallet address
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A non-null pointer to a [`NomosNode`] instance.
|
||||
/// - `wallet_address`: A non-null pointer to the public key bytes of the wallet
|
||||
/// address to query.
|
||||
/// - `optional_tip`: An optional pointer to the header ID to query the balance
|
||||
/// at. If null, the current tip will be used.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A [`ValueResult`] containing the balance on success, or an
|
||||
/// [`OperationStatus`] error on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it dereferences raw pointers. The caller
|
||||
/// must ensure that all pointers are valid.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn get_balance(
|
||||
node: *const NomosNode,
|
||||
wallet_address: *const u8,
|
||||
optional_tip: *const HeaderId,
|
||||
) -> BalanceResult {
|
||||
if node.is_null() {
|
||||
eprintln!("[get_balance] Received a null `node` pointer. Exiting.");
|
||||
return BalanceResult::from_error(OperationStatus::NullPtr);
|
||||
}
|
||||
if wallet_address.is_null() {
|
||||
eprintln!("[get_balance] Received a null `wallet_address` pointer. Exiting.");
|
||||
return BalanceResult::from_error(OperationStatus::NullPtr);
|
||||
}
|
||||
|
||||
let node = unsafe { &*node };
|
||||
let tip = if optional_tip.is_null() {
|
||||
match get_cryptarchia_info_sync(node) {
|
||||
Ok(cryptarchia_info) => cryptarchia_info.tip,
|
||||
Err(error) => return BalanceResult::from_error(error),
|
||||
}
|
||||
} else {
|
||||
nomos_core::header::HeaderId::from(unsafe { *optional_tip })
|
||||
};
|
||||
let wallet_address_bytes = unsafe { std::slice::from_raw_parts(wallet_address, 32) };
|
||||
let wallet_address = ZkPublicKey::from(BigUint::from_bytes_le(wallet_address_bytes));
|
||||
|
||||
match get_balance_sync(node, tip, wallet_address) {
|
||||
Ok(Some(balance)) => BalanceResult::from_value(balance),
|
||||
Ok(None) => BalanceResult::from_error(OperationStatus::NotFound),
|
||||
Err(status) => BalanceResult::from_error(status),
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TransferFundsArguments {
|
||||
pub optional_tip: *const HeaderId,
|
||||
pub change_public_key: *const u8,
|
||||
pub funding_public_keys: *const *const u8,
|
||||
pub funding_public_keys_len: usize,
|
||||
pub recipient_public_key: *const u8,
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
impl TransferFundsArguments {
|
||||
/// Validates the arguments of the [`TransferFundsArguments`] struct.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` indicating success or containing an error message and status.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it dereferences raw pointers. The caller
|
||||
/// must ensure that all pointers are valid.
|
||||
pub unsafe fn validate(&self) -> Result<(), (String, OperationStatus)> {
|
||||
if self.change_public_key.is_null() {
|
||||
return Err((
|
||||
"TransferFunds contains a null `change_public_key` pointer.".to_owned(),
|
||||
OperationStatus::NullPtr,
|
||||
));
|
||||
}
|
||||
if self.funding_public_keys.is_null() {
|
||||
return Err((
|
||||
"TransferFunds contains a null `funding_public_keys` pointer.".to_owned(),
|
||||
OperationStatus::NullPtr,
|
||||
));
|
||||
}
|
||||
|
||||
for i in 0..self.funding_public_keys_len {
|
||||
let funding_public_key_pointer = unsafe { self.funding_public_keys.add(i) };
|
||||
let funding_public_key = unsafe { *funding_public_key_pointer };
|
||||
if funding_public_key.is_null() {
|
||||
let error_message =
|
||||
format!("TransferFunds contains a null pointer at `funding_public_keys[{i}]`.");
|
||||
return Err((error_message, OperationStatus::NullPtr));
|
||||
}
|
||||
}
|
||||
|
||||
if self.recipient_public_key.is_null() {
|
||||
return Err((
|
||||
"TransferFunds contains a null `recipient_public_key` pointer.".to_owned(),
|
||||
OperationStatus::NullPtr,
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer funds from some addresses to another.
|
||||
///
|
||||
/// This is a synchronous wrapper around [`WalletApi::transfer_funds`].
|
||||
///
|
||||
/// This function does not validate the arguments. It assumes they have already
|
||||
/// been validated.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A [`NomosNode`] instance.
|
||||
/// - `tip`: The header ID at which to perform the transfer.
|
||||
/// - `change_public_key`: The public key to receive any change from the
|
||||
/// transaction.
|
||||
/// - `funding_public_keys`: A vector of public keys to fund the transaction.
|
||||
/// - `recipient_public_key`: The public key of the recipient.
|
||||
/// - `amount`: The amount to transfer.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` containing a [`SignedMantleTx`] on success, or an
|
||||
/// [`OperationStatus`] error on failure.
|
||||
pub(crate) fn transfer_funds_sync(
|
||||
node: &NomosNode,
|
||||
tip: nomos_core::header::HeaderId,
|
||||
change_public_key: ZkPublicKey,
|
||||
funding_public_keys: Vec<ZkPublicKey>,
|
||||
recipient_public_key: ZkPublicKey,
|
||||
amount: u64,
|
||||
) -> Result<SignedMantleTx, OperationStatus> {
|
||||
let Ok(runtime) = tokio::runtime::Runtime::new() else {
|
||||
eprintln!("[transfer_funds_sync] Failed to create tokio runtime. Aborting.");
|
||||
return Err(OperationStatus::RuntimeError);
|
||||
};
|
||||
|
||||
runtime
|
||||
.block_on(async {
|
||||
let api = WalletApi::<WalletService<_, _, _, _, _>, _>::from_overwatch_handle(
|
||||
node.get_overwatch_handle(),
|
||||
)
|
||||
.await;
|
||||
api.transfer_funds(
|
||||
tip,
|
||||
change_public_key,
|
||||
funding_public_keys,
|
||||
recipient_public_key,
|
||||
amount,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.map_err(|_| OperationStatus::DynError)
|
||||
}
|
||||
|
||||
pub type TransferFundsResult = ValueResult<Hash, OperationStatus>;
|
||||
|
||||
/// Transfer funds from some addresses to another.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `node`: A non-null pointer to a [`NomosNode`] instance.
|
||||
/// - `arguments`: A non-null pointer to a [`TransferFundsArguments`] struct
|
||||
/// containing the transaction arguments.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A [`TransferFundsResult`] containing a pointer to a [`Hash`] where the
|
||||
/// transaction hash will be written on success, or an [`OperationStatus`] error
|
||||
/// on failure. The hash will be written in little-endian format.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it dereferences raw pointers. The caller
|
||||
/// must ensure that all pointers are valid.
|
||||
///
|
||||
/// # Memory Management
|
||||
///
|
||||
/// This function allocates memory for the output [`CryptarchiaInfo`] struct.
|
||||
/// The caller must free this memory using the [`free_cryptarchia_info`]
|
||||
/// function.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn transfer_funds(
|
||||
node: *const NomosNode,
|
||||
arguments: *const TransferFundsArguments,
|
||||
) -> TransferFundsResult {
|
||||
if node.is_null() {
|
||||
eprintln!("[transfer_funds] Received a null `node` pointer. Exiting.");
|
||||
return TransferFundsResult::from_error(OperationStatus::NullPtr);
|
||||
}
|
||||
if arguments.is_null() {
|
||||
eprintln!("[transfer_funds] Received a null `arguments` pointer. Exiting.");
|
||||
return TransferFundsResult::from_error(OperationStatus::NullPtr);
|
||||
}
|
||||
let arguments = unsafe { &*arguments };
|
||||
if let Err((error_message, status)) = unsafe { arguments.validate() } {
|
||||
eprintln!("[transfer_funds] {error_message} Exiting.");
|
||||
return TransferFundsResult::from_error(status);
|
||||
}
|
||||
|
||||
let node = unsafe { &*node };
|
||||
let tip = if arguments.optional_tip.is_null() {
|
||||
match get_cryptarchia_info_sync(node) {
|
||||
Ok(cryptarchia_info) => cryptarchia_info.tip,
|
||||
Err(status) => {
|
||||
eprintln!("[transfer_funds] Failed to get cryptarchia info. Aborting.");
|
||||
return TransferFundsResult::from_error(status);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nomos_core::header::HeaderId::from(unsafe { *arguments.optional_tip })
|
||||
};
|
||||
let change_public_key = {
|
||||
let change_public_key_bytes =
|
||||
unsafe { std::slice::from_raw_parts(arguments.change_public_key, 32) };
|
||||
ZkPublicKey::from(BigUint::from_bytes_le(change_public_key_bytes))
|
||||
};
|
||||
let funding_public_keys = {
|
||||
let funding_public_keys_pointers = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
arguments.funding_public_keys,
|
||||
arguments.funding_public_keys_len,
|
||||
)
|
||||
};
|
||||
funding_public_keys_pointers
|
||||
.iter()
|
||||
.map(|funding_public_key_pointer| {
|
||||
let funding_public_key_bytes =
|
||||
unsafe { std::slice::from_raw_parts(*funding_public_key_pointer, 32) };
|
||||
ZkPublicKey::from(BigUint::from_bytes_le(funding_public_key_bytes))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let recipient_public_key = {
|
||||
let recipient_public_key_bytes =
|
||||
unsafe { std::slice::from_raw_parts(arguments.recipient_public_key, 32) };
|
||||
ZkPublicKey::from(BigUint::from_bytes_le(recipient_public_key_bytes))
|
||||
};
|
||||
let amount = Value::from(arguments.amount);
|
||||
|
||||
match transfer_funds_sync(
|
||||
node,
|
||||
tip,
|
||||
change_public_key,
|
||||
funding_public_keys,
|
||||
recipient_public_key,
|
||||
amount,
|
||||
) {
|
||||
Ok(transaction) => {
|
||||
let transaction_hash = transaction.hash().as_signing_bytes();
|
||||
let Ok(transaction_hash_array) = transaction_hash.iter().as_slice().try_into() else {
|
||||
eprintln!("[transfer_funds] Failed to convert transaction hash to array. Exiting.");
|
||||
return TransferFundsResult::from_error(OperationStatus::RuntimeError);
|
||||
};
|
||||
TransferFundsResult::from_value(transaction_hash_array)
|
||||
}
|
||||
Err(status) => TransferFundsResult::from_error(status),
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the memory allocated for a [`Hash`] value.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `pointer`: A pointer to the [`Hash`] to be freed.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn free_transfer_funds(pointer: *mut Hash) {
|
||||
free::<Hash>(pointer);
|
||||
}
|
||||
@ -1,7 +1,30 @@
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
#[repr(C)]
|
||||
pub enum NomosNodeErrorCode {
|
||||
#[default]
|
||||
None = 0x0,
|
||||
CouldNotInitialize = 0x1,
|
||||
StopError = 0x2,
|
||||
NullPtr = 0x3,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub enum OperationStatus {
|
||||
#[default]
|
||||
Ok = 0x0,
|
||||
NotFound = 0x1,
|
||||
NullPtr = 0x2,
|
||||
RelayError = 0x3,
|
||||
ChannelSendError = 0x4,
|
||||
ChannelReceiveError = 0x5,
|
||||
ServiceError = 0x6,
|
||||
RuntimeError = 0x7,
|
||||
DynError = 0x8,
|
||||
}
|
||||
|
||||
impl OperationStatus {
|
||||
pub fn is_ok(&self) -> bool {
|
||||
*self == Self::Ok
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ impl NomosNode {
|
||||
|
||||
// Helper methods to safely access the inner types
|
||||
#[must_use]
|
||||
const fn get_overwatch_handle(&self) -> &OverwatchHandle<RuntimeServiceId> {
|
||||
pub(crate) const fn get_overwatch_handle(&self) -> &OverwatchHandle<RuntimeServiceId> {
|
||||
unsafe {
|
||||
self.overwatch
|
||||
.cast::<NomosOverwatch>()
|
||||
@ -39,7 +39,7 @@ impl NomosNode {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn get_runtime_handle(&self) -> &Handle {
|
||||
pub(crate) fn get_runtime_handle(&self) -> &Handle {
|
||||
unsafe {
|
||||
self.runtime
|
||||
.cast::<Runtime>()
|
||||
|
||||
@ -5,11 +5,12 @@ use nomos_core::{
|
||||
};
|
||||
use overwatch::{
|
||||
DynError,
|
||||
overwatch::OverwatchHandle,
|
||||
services::{AsServiceId, ServiceData, relay::OutboundRelay},
|
||||
};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::{WalletMsg, WalletServiceSettings};
|
||||
use crate::{WalletMsg, WalletService, WalletServiceSettings};
|
||||
|
||||
pub trait WalletServiceData:
|
||||
ServiceData<Settings = WalletServiceSettings, Message = WalletMsg>
|
||||
@ -21,7 +22,7 @@ pub trait WalletServiceData:
|
||||
}
|
||||
|
||||
impl<Kms, Cryptarchia, Tx, Storage, RuntimeServiceId> WalletServiceData
|
||||
for crate::WalletService<Kms, Cryptarchia, Tx, Storage, RuntimeServiceId>
|
||||
for WalletService<Kms, Cryptarchia, Tx, Storage, RuntimeServiceId>
|
||||
{
|
||||
type Kms = Kms;
|
||||
type Cryptarchia = Cryptarchia;
|
||||
@ -50,6 +51,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub async fn from_overwatch_handle(handle: &OverwatchHandle<RuntimeServiceId>) -> Self {
|
||||
let relay = handle.relay::<Wallet>().await.unwrap();
|
||||
Self::new(relay)
|
||||
}
|
||||
|
||||
pub async fn get_balance(
|
||||
&self,
|
||||
tip: HeaderId,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user