diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 679dda7ad2..918a9ae679 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -55,7 +55,6 @@ jobs:
!github.event.pull_request.head.repo.fork
uses: fluencelabs/decider/.github/workflows/tests.yml@main
with:
- ref: "feature/brnd-19"
test-cargo-dependencies: |
[
{
diff --git a/Cargo.lock b/Cargo.lock
index e1f37c3680..efc6f9bd1b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1359,6 +1359,7 @@ dependencies = [
"hex-utils",
"jsonrpsee 0.22.5",
"libipld",
+ "libp2p-identity",
"log-utils",
"mockito",
"particle-args",
@@ -2127,9 +2128,9 @@ dependencies = [
[[package]]
name = "decider-distro"
-version = "0.7.0"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bafeb2d55c8843ac62a86ae40b229a834bc6a9b89da46e7ffbd551cec43bd159"
+checksum = "1f87fc5b72d3931ea4017ee61d7c99a7f98ec6f337b877f4e78fe14a708cb1ec"
dependencies = [
"built 0.7.1",
"fluence-spell-dtos",
@@ -5923,10 +5924,14 @@ dependencies = [
name = "nox-tests"
version = "0.1.0"
dependencies = [
+ "alloy-primitives",
+ "alloy-sol-types",
"aquamarine",
"base64 0.21.7",
"blake3",
"bs58",
+ "chain-connector",
+ "chain-data",
"clarity",
"connected-client",
"connection-pool",
@@ -5941,6 +5946,7 @@ dependencies = [
"fstrings",
"futures",
"hex",
+ "hex-utils",
"humantime-serde",
"itertools 0.13.0",
"json-utils",
@@ -5968,7 +5974,6 @@ dependencies = [
"service-modules",
"sorcerer",
"spell-event-bus",
- "subnet-resolver",
"system-services",
"tempfile",
"test-constants",
@@ -6370,7 +6375,6 @@ dependencies = [
"serde",
"serde_json",
"service-modules",
- "subnet-resolver",
"tempfile",
"thiserror",
"tokio",
@@ -8342,23 +8346,6 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-[[package]]
-name = "subnet-resolver"
-version = "0.1.0"
-dependencies = [
- "chain-data",
- "ethabi",
- "eyre",
- "hex",
- "hex-utils",
- "jsonrpsee 0.22.5",
- "libp2p-identity",
- "serde",
- "serde_json",
- "thiserror",
- "tokio",
-]
-
[[package]]
name = "subtle"
version = "2.5.0"
@@ -8573,6 +8560,7 @@ name = "test-utils"
version = "0.3.0"
dependencies = [
"base64 0.21.7",
+ "clarity",
"connected-client",
"cpu-utils",
"eyre",
@@ -8581,6 +8569,7 @@ dependencies = [
"maplit",
"particle-args",
"serde_json",
+ "server-config",
"service-modules",
"test-constants",
"tokio",
diff --git a/Cargo.toml b/Cargo.toml
index 9c72fb4e31..a7d07a8b12 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,7 +31,6 @@ members = [
"crates/health",
"sorcerer",
"crates/nox-tests",
- "crates/subnet-resolver",
"nox",
"aquamarine",
"particle-protocol",
@@ -96,7 +95,6 @@ spell-storage = { path = "spell-storage" }
particle-execution = { path = "particle-execution" }
system-services = { path = "crates/system-services" }
health = { path = "crates/health" }
-subnet-resolver = { path = "crates/subnet-resolver" }
hex-utils = { path = "crates/hex-utils" }
chain-data = { path = "crates/chain-data" }
chain-listener = { path = "crates/chain-listener" }
diff --git a/crates/chain-connector/Cargo.toml b/crates/chain-connector/Cargo.toml
index 983f8f69f4..4cfed47b52 100644
--- a/crates/chain-connector/Cargo.toml
+++ b/crates/chain-connector/Cargo.toml
@@ -10,7 +10,7 @@ particle-builtins = { workspace = true }
particle-execution = { workspace = true }
particle-args = { workspace = true }
chain-data = { workspace = true }
-jsonrpsee = { workspace = true, features = ["macros", "ws-client"] }
+jsonrpsee = { workspace = true, features = ["macros", "ws-client", "http-client"] }
eyre = { workspace = true }
fluence-libp2p = { workspace = true }
serde_json = { workspace = true }
@@ -29,6 +29,7 @@ const-hex = { workspace = true }
serde = { workspace = true }
async-trait = { workspace = true }
libipld = { workspace = true }
+libp2p-identity = { workspace = true }
[dev-dependencies]
mockito = { workspace = true }
diff --git a/crates/chain-connector/src/builtins.rs b/crates/chain-connector/src/builtins.rs
new file mode 100644
index 0000000000..5fc34139ff
--- /dev/null
+++ b/crates/chain-connector/src/builtins.rs
@@ -0,0 +1,197 @@
+/*
+ * Nox Fluence Peer
+ *
+ * Copyright (C) 2024 Fluence DAO
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+use crate::types::{SubnetResolveResult, TxReceiptResult, TxStatus, Worker};
+use crate::{ChainConnector, HttpChainConnector};
+use ccp_shared::types::CUID;
+use futures::FutureExt;
+use particle_args::{Args, JError};
+use particle_builtins::{wrap, CustomService};
+use particle_execution::{ParticleParams, ServiceFunction};
+use serde_json::json;
+use serde_json::Value as JValue;
+use std::collections::HashMap;
+use std::sync::Arc;
+use types::peer_scope::WorkerId;
+use types::DealId;
+
+// macro to generate a closure for a builtin function
+macro_rules! make_builtin_closure {
+ ($connector:expr, $function:ident) => {{
+ let connector = $connector.clone();
+ ServiceFunction::Immut(Box::new(move |args, params| {
+ let connector = connector.clone();
+ async move { wrap($function(connector, args, params).await) }.boxed()
+ }))
+ }};
+}
+
+pub(crate) fn make_connector_builtins(
+ connector: Arc,
+) -> HashMap {
+ let mut builtins = HashMap::new();
+ builtins.insert(
+ "connector".to_string(),
+ CustomService::new(
+ vec![
+ (
+ "get_deals",
+ make_builtin_closure!(connector, get_deals_builtin),
+ ),
+ (
+ "register_worker",
+ make_builtin_closure!(connector, register_worker_builtin),
+ ),
+ (
+ "get_tx_receipts",
+ make_builtin_closure!(connector, get_tx_receipts_builtin),
+ ),
+ ],
+ None,
+ ),
+ );
+ // Legacy service name; Can be deprecated and moved to connector in the future
+ builtins.insert(
+ "subnet".to_string(),
+ CustomService::new(
+ vec![(
+ "resolve",
+ make_builtin_closure!(connector, resolve_subnet_builtin),
+ )],
+ None,
+ ),
+ );
+ builtins
+}
+
+async fn get_deals_builtin(
+ connector: Arc,
+ _args: Args,
+ params: ParticleParams,
+) -> Result {
+ if params.init_peer_id != connector.host_id {
+ return Err(JError::new(
+ "Only the root worker can call connector.get_deals",
+ ));
+ }
+
+ let deals = connector
+ .get_deals()
+ .await
+ .map_err(|err| JError::new(format!("Failed to get deals: {err}")))?;
+ Ok(json!(deals))
+}
+
+async fn register_worker_builtin(
+ connector: Arc,
+ args: Args,
+ params: ParticleParams,
+) -> Result {
+ if params.init_peer_id != connector.host_id {
+ return Err(JError::new(
+ "Only the root worker can call connector.register_worker",
+ ));
+ }
+
+ let mut args = args.function_args.into_iter();
+ let deal_id: DealId = Args::next("deal_id", &mut args)?;
+ let worker_id: WorkerId = Args::next("worker_id", &mut args)?;
+ let cu_ids: Vec = Args::next("cu_id", &mut args)?;
+
+ if cu_ids.len() != 1 {
+ return Err(JError::new("Only one cu_id is allowed"));
+ }
+
+ let tx_hash = connector
+ .register_worker(&deal_id, worker_id, cu_ids[0])
+ .await
+ .map_err(|err| JError::new(format!("Failed to register worker: {err}")))?;
+ Ok(json!(tx_hash))
+}
+
+async fn get_tx_receipts_builtin(
+ connector: Arc,
+ args: Args,
+ params: ParticleParams,
+) -> Result {
+ if params.init_peer_id != connector.host_id {
+ return Err(JError::new(
+ "Only the root worker can call connector.get_tx_receipt",
+ ));
+ }
+
+ let mut args = args.function_args.into_iter();
+
+ let tx_hashes: Vec = Args::next("tx_hashes", &mut args)?;
+
+ let receipts = connector
+ .get_tx_receipts(tx_hashes)
+ .await
+ .map_err(|err| JError::new(format!("Failed to get tx receipts: {err}")))?
+ .into_iter()
+ .map(|tx_receipt| match tx_receipt {
+ Ok(receipt) => match receipt {
+ TxStatus::Pending => TxReceiptResult::pending(),
+ TxStatus::Processed(receipt) => TxReceiptResult::processed(receipt),
+ },
+ Err(err) => TxReceiptResult::error(err.to_string()),
+ })
+ .collect::>();
+
+ Ok(json!(receipts))
+}
+
+async fn resolve_subnet_builtin(
+ connector: Arc,
+ args: Args,
+ _params: ParticleParams,
+) -> Result {
+ let deal_id: String = Args::next("deal_id", &mut args.function_args.into_iter())?;
+ let deal_id = DealId::from(deal_id);
+
+ let workers: eyre::Result> = try {
+ if !deal_id.is_valid() {
+ Err(eyre::eyre!(
+ "Invalid deal id '{}': invalid length",
+ deal_id.as_str()
+ ))?;
+ }
+
+ let units = connector.get_deal_compute_units(&deal_id).await?;
+ let workers: Result, _> = units
+ .into_iter()
+ .map(|unit| Worker::try_from(unit))
+ .collect();
+ workers?
+ };
+
+ let result = match workers {
+ Ok(workers) => SubnetResolveResult {
+ success: true,
+ workers,
+ error: vec![],
+ },
+ Err(err) => SubnetResolveResult {
+ success: false,
+ workers: vec![],
+ error: vec![format!("{}", err)],
+ },
+ };
+
+ Ok(json!(result))
+}
diff --git a/crates/chain-connector/src/connector.rs b/crates/chain-connector/src/connector.rs
index ec97f8f17e..ae7b5d8467 100644
--- a/crates/chain-connector/src/connector.rs
+++ b/crates/chain-connector/src/connector.rs
@@ -27,34 +27,30 @@ use std::sync::Arc;
use ccp_shared::types::{Difficulty, GlobalNonce, LocalNonce, ResultHash, CUID};
use clarity::{Transaction, Uint256};
use eyre::eyre;
-use futures::FutureExt;
use jsonrpsee::core::async_trait;
use jsonrpsee::core::client::{BatchResponse, ClientT};
use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder};
use jsonrpsee::http_client::HttpClientBuilder;
use jsonrpsee::rpc_params;
-use serde_json::Value as JValue;
use serde_json::{json, Value};
use tokio::sync::Mutex;
+use crate::builtins::make_connector_builtins;
+use crate::error::process_response;
+use crate::eth_call::EthCall;
+use crate::types::*;
use crate::ConnectorError::{FieldNotFound, InvalidU256, ResponseParseError};
use crate::Deal::CIDV1;
+use crate::Offer::{ComputePeer, ComputeUnit};
use crate::{CCStatus, Capacity, CommitmentId, Core, Deal, Offer};
use chain_data::{peer_id_to_bytes, BlockHeader};
use fluence_libp2p::PeerId;
use hex_utils::{decode_hex, encode_hex_0x};
-use particle_args::{Args, JError};
-use particle_builtins::{wrap, CustomService};
-use particle_execution::{ParticleParams, ServiceFunction};
+use particle_builtins::CustomService;
use server_config::ChainConfig;
use types::peer_scope::WorkerId;
use types::DealId;
-use crate::error::process_response;
-use crate::eth_call::EthCall;
-use crate::types::*;
-use crate::Offer::{ComputePeer, ComputeUnit};
-
#[async_trait]
pub trait ChainConnector: Send + Sync {
async fn get_current_commitment_id(&self) -> Result