Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
### Removed

- Gateway no longer performs a public port reachability check during registration. Relays connect outbound to the load balancers over WebSocket, so they no longer need a publicly routable inbound port.
- The platform no longer serves its API under a random UUID path prefix; all routes are now available directly at `/`.

## [1.0.0] - 2026-05-15

Expand Down
17 changes: 7 additions & 10 deletions crates/integration_tests/src/gateway/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ use blockfrost_gateway::{
rate_limit::{self, RegisterRateLimiter},
types::AssetName,
};
use blockfrost_platform::{
hydra_client, icebreakers::manager::IcebreakersManager, server::state::ApiPrefix,
};
use blockfrost_platform::{hydra_client, icebreakers::manager::IcebreakersManager};
use reqwest::{Client, Response};
use serde::Deserialize;
use serde_json::json;
Expand Down Expand Up @@ -238,25 +236,24 @@ pub async fn wait_for_ready(client: &Client, url: &str, timeout: Duration) -> Re
/// Common setup: start a `TestGateway` + Platform, wire through `IcebreakersManager`.
///
/// Returns `(gateway, client, base_url_with_prefix)` for making requests.
pub async fn setup() -> (TestGateway, Client, String, ApiPrefix) {
pub async fn setup() -> (TestGateway, Client, String, uuid::Uuid) {
crate::initialize_logging();

let gw = TestGateway::start().await;
let gateway_url = format!("http://{}", gw.addr);

let (app, _, _, icebreakers_api, api_prefix) =
crate::platform::build_app_non_solitary(Some(gateway_url))
.await
.expect("Failed to build the application");
let (app, _, _, icebreakers_api) = crate::platform::build_app_non_solitary(Some(gateway_url))
.await
.expect("Failed to build the application");

let icebreakers_api = icebreakers_api.expect("icebreakers_api should be Some");
let api_prefix = icebreakers_api.api_prefix();
let health_errors = Arc::new(Mutex::new(vec![]));

let manager = IcebreakersManager::new(
icebreakers_api,
health_errors,
app,
api_prefix.clone(),
bf_common::DEFAULT_MAX_BODY_BYTES,
);

Expand All @@ -270,7 +267,7 @@ pub async fn setup() -> (TestGateway, Client, String, ApiPrefix) {
manager.run((kex_req_rx, kex_resp_tx, terminate_tx)).await;

let client = Client::new();
let base = format!("http://{}{}", gw.addr, api_prefix);
let base = format!("http://{}/{}", gw.addr, api_prefix);

// Wait for the relay to be ready and for the Platform root to return 200.
wait_for_ready(&client, &format!("{base}/"), Duration::from_secs(30)).await;
Expand Down
7 changes: 1 addition & 6 deletions crates/integration_tests/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ use bf_node::pool::NodePool;
use blockfrost_platform::config::{Config, DataNodeConfig, IcebreakersConfig, Mode};
use blockfrost_platform::genesis::genesis;
use blockfrost_platform::{
AppError, health_monitor,
icebreakers::api::IcebreakersAPI,
server::{build, state::ApiPrefix},
AppError, health_monitor, icebreakers::api::IcebreakersAPI, server::build,
};
use std::{env, sync::Arc, time::Duration};

Expand Down Expand Up @@ -47,7 +45,6 @@ pub async fn build_app() -> Result<
NodePool,
health_monitor::HealthMonitor,
Option<Arc<IcebreakersAPI>>,
ApiPrefix,
),
AppError,
> {
Expand All @@ -64,7 +61,6 @@ pub async fn build_app_non_solitary(
NodePool,
health_monitor::HealthMonitor,
Option<Arc<IcebreakersAPI>>,
ApiPrefix,
),
AppError,
> {
Expand Down Expand Up @@ -120,7 +116,6 @@ pub async fn build_app_with_data_node(
NodePool,
health_monitor::HealthMonitor,
Option<Arc<IcebreakersAPI>>,
ApiPrefix,
),
AppError,
> {
Expand Down
4 changes: 1 addition & 3 deletions crates/integration_tests/tests/e2e_websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ async fn test_ws_invalid_credentials_rejected() {
};
let config = test_config(Some(icebreakers_config));

let (app, _, _, icebreakers_api, api_prefix) =
build(config).await.expect("Failed to build app");
let (app, _, _, icebreakers_api) = build(config).await.expect("Failed to build app");

let icebreakers_api = icebreakers_api.expect("icebreakers_api should be Some");
let health_errors = Arc::new(Mutex::new(vec![]));
Expand All @@ -126,7 +125,6 @@ async fn test_ws_invalid_credentials_rejected() {
icebreakers_api,
health_errors.clone(),
app,
api_prefix,
bf_common::DEFAULT_MAX_BODY_BYTES,
);

Expand Down
6 changes: 3 additions & 3 deletions crates/integration_tests/tests/platform_data_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async fn test_data_node_health_monitoring() {
initialize_logging();

let mock = MockDataNode::healthy().await;
let (app, _, _, _, _) = build_app_with_data_node(mock.url)
let (app, _, _, _) = build_app_with_data_node(mock.url)
.await
.expect("Failed to build the application");

Expand All @@ -59,7 +59,7 @@ async fn test_data_node_unhealthy_status() {
initialize_logging();

let mock = MockDataNode::unhealthy().await;
let (app, _, _, _, _) = build_app_with_data_node(mock.url)
let (app, _, _, _) = build_app_with_data_node(mock.url)
.await
.expect("Failed to build the application");

Expand All @@ -81,7 +81,7 @@ async fn test_data_node_unreachable() {
initialize_logging();

let mock = MockDataNode::unreachable();
let (app, _, _, _, _) = build_app_with_data_node(mock.url)
let (app, _, _, _) = build_app_with_data_node(mock.url)
.await
.expect("Failed to build the application");

Expand Down
2 changes: 1 addition & 1 deletion crates/integration_tests/tests/platform_metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tower::ServiceExt;
async fn test_route_metrics() {
initialize_logging();

let (app, _, _, _, _) = build_app().await.expect("Failed to build the application");
let (app, _, _, _) = build_app().await.expect("Failed to build the application");

// Test without trailing slash
let response = app
Expand Down
2 changes: 1 addition & 1 deletion crates/integration_tests/tests/platform_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tower::ServiceExt;
async fn test_route_root() {
initialize_logging();

let (app, _, _, _, _) = build_app().await.expect("Failed to build the application");
let (app, _, _, _) = build_app().await.expect("Failed to build the application");

let response = app
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
Expand Down
8 changes: 4 additions & 4 deletions crates/integration_tests/tests/platform_submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use tower::ServiceExt;
#[ntest::timeout(120_000)]
async fn test_route_submit_cbor_error() {
initialize_logging();
let (app, _, _, _, _) = build_app().await.expect("Failed to build the application");
let (app, _, _, _) = build_app().await.expect("Failed to build the application");

let tx = "AAAAAA";

Expand Down Expand Up @@ -48,7 +48,7 @@ async fn test_route_submit_cbor_error() {
#[ntest::timeout(120_000)]
async fn test_route_submit_error() {
initialize_logging();
let (app, _, _, _, _) = build_app().await.expect("Failed to build the application");
let (app, _, _, _) = build_app().await.expect("Failed to build the application");

let tx = "84a300d90102818258205176274bef11d575edd6aa72392aaf993a07f736e70239c1fb22d4b1426b22bc01018282583900ddf1eb9ce2a1561e8f156991486b97873fb6969190cbc99ddcb3816621dcb03574152623414ed354d2d8f50e310f3f2e7d167cb20e5754271a003d09008258390099a5cb0fa8f19aba38cacf8a243d632149129f882df3a8e67f6bd512bcb0cde66a545e9fbc7ca4492f39bca1f4f265cc1503b4f7d6ff205c1b000000024f127a7c021a0002a2ada100d90102818258208b83e59abc9d7a66a77be5e0825525546a595174f8b929f164fcf5052d7aab7b5840709c64556c946abf267edd90b8027343d065193ef816529d8fa7aa2243f1fd2ec27036a677974199e2264cb582d01925134b9a20997d5a734da298df957eb002f5f6";

Expand Down Expand Up @@ -92,7 +92,7 @@ async fn test_route_submit_error() {
#[ntest::timeout(120_000)]
async fn test_route_submit_agent_dequeu() {
initialize_logging();
let (app, _, _, _, _) = build_app().await.expect("Failed to build the application");
let (app, _, _, _) = build_app().await.expect("Failed to build the application");

let tx = "84a800848258204c16d304e6d531c59afd87a9199b7bb4175bc131b3d6746917901046b662963c00825820893c3f630c0b2db16d041c388aa0d58746ccbbc44133b2d7a3127a72c79722f1018258200998adb591c872a241776e39fe855e04b2d7c361008e94c582f59b6b6ccc452c028258208380ce7240ba59187f6450911f74a70cf3d2749228badb2e7cd10fb6499355f503018482581d61e15900a9a62a8fb01f936a25bf54af209c7ed1248c4e5abd05ec4e76821a0023ba63a1581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b5900a300581d71cba5c6770fe7b30ebc1fa32f01938c150513211360ded23ac76e36b301821a006336d5a3581c239075b83c03c2333eacd0b0beac6b8314f11ce3dc0c047012b0cad4a144706f6f6c01581c3547b4325e495d529619335603ababde10025dceafa9ed34b1fb6611a158208b284793d3bd4967244a2ddd68410d56d06d36ac8d201429b937096a2e8234bc1b7ffffffffffade6b581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b59195e99028201d818583ad8799fd8799f4040ffd8799f581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c23545484f534b59ff1a006336d5195e99ff825839016d06090559d8ed2988aa5b2fff265d668cf552f4f62278c0128f816c0a48432e080280d0d9b15edb65563995f97ce236035afea568e660d1821a00118f32a1581c2f8b2d1f384485896f38406173fa11df2a4ce53b4b0886138b76597aa1476261746368657201825839016d06090559d8ed2988aa5b2fff265d668cf552f4f62278c0128f816c0a48432e080280d0d9b15edb65563995f97ce236035afea568e660d11a06d9f713021a000ab9e00b582027f17979d848d6472896266dd8bf39f7251ca23798713464bc407bf637286c230d81825820cf5de9189b958f8ad64c1f1837c2fa4711d073494598467a1c1a59589393eae20310825839016d06090559d8ed2988aa5b2fff265d668cf552f4f62278c0128f816c0a48432e080280d0d9b15edb65563995f97ce236035afea568e660d11a08666c75111a001016d01282825820bf93dc59c10c19c35210c2414779d7391ca19128cc7b13794ea85af5ff835f59008258201c37df764f8261edce8678b197767668a91d544b2b203fb5d0cf9acc10366e7600a200818258200eabfa083d7969681d2fc8e825a5f79e1c40f03aeac46ecd94bf5c5790db1bc058409a029ddd3cdde65598bb712c640ea63eeebfee526ce49bd0983b4d1fdca858481ddf931bf0354552cc0a7d3365e2f03fdb457c0466cea8b371b645f9b6d0c2010582840001d8799fd8799f011a006336d5195e991b7ffffffffffade6bd8799f1a000539e7ff01ffff821a000b46e41a0a7f3ca4840003d87d80821a002dccfe1a28868be8f5f6";

Expand Down Expand Up @@ -127,7 +127,7 @@ async fn test_route_submit_agent_dequeu() {
#[ntest::timeout(120_000)]
async fn test_route_submit_success() {
initialize_logging();
let (app, _, _, _, _) = build_app().await.expect("Failed to build the application");
let (app, _, _, _) = build_app().await.expect("Failed to build the application");
let blockfrost_client = get_blockfrost_client();
let tx = build_tx(&blockfrost_client).await.unwrap();

Expand Down
24 changes: 15 additions & 9 deletions crates/platform/src/icebreakers/api.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::config::Config;
use crate::{load_balancer::LoadBalancerConfig, server::state::ApiPrefix};
use crate::load_balancer::LoadBalancerConfig;
use bf_common::errors::AppError;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Arc;
use tracing::{info, warn};
use uuid::Uuid;

#[derive(Debug)]
pub struct IcebreakersAPI {
Expand All @@ -15,7 +16,8 @@ pub struct IcebreakersAPI {
mode: String,
port: u16,
reward_address: String,
api_prefix: ApiPrefix,
/// A random identifier sent during registration. The gateway uses it to identify this relay
api_prefix: Uuid,
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -43,10 +45,7 @@ pub struct SuccessResponse {

impl IcebreakersAPI {
/// Creates a new `IcebreakersAPI` instance or logs a warning if not configured
pub async fn new(
config: &Config,
api_prefix: ApiPrefix,
) -> Result<Option<Arc<Self>>, AppError> {
pub async fn new(config: &Config) -> Result<Option<Arc<Self>>, AppError> {
match &config.icebreakers_config {
Some(icebreakers_config) => {
let api_url = icebreakers_config
Expand Down Expand Up @@ -75,7 +74,7 @@ impl IcebreakersAPI {
mode: config.mode.to_string(),
port: config.server_port,
reward_address: icebreakers_config.reward_address.clone(),
api_prefix,
api_prefix: Uuid::new_v4(),
};

let icebreakers_api = Arc::new(icebreakers_api);
Expand All @@ -100,6 +99,10 @@ impl IcebreakersAPI {
}
}

pub fn api_prefix(&self) -> Uuid {
self.api_prefix
}

/// Registers with the Icebreakers API
pub async fn register(&self) -> Result<SuccessResponse, AppError> {
let url = format!("{}/register", self.base_url);
Expand All @@ -108,7 +111,7 @@ impl IcebreakersAPI {
"mode": self.mode,
"port": self.port,
"reward_address": self.reward_address,
"api_prefix": self.api_prefix.0.unwrap_or_default(),
"api_prefix": self.api_prefix,
});

let response = self
Expand All @@ -124,7 +127,10 @@ impl IcebreakersAPI {
AppError::Registration(format!("Failed to parse success response: {e}"))
})?;

info!("successfully registered with Icebreakers API");
info!(
"successfully registered with Icebreakers API (route: {})",
success_response.route
);

// In case we get a URI without a protocol (http: or https: or ws: or wss:):
let fallback_proto = if self.base_url.starts_with("https:") {
Expand Down
5 changes: 0 additions & 5 deletions crates/platform/src/icebreakers/manager.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::icebreakers::api::IcebreakersAPI;
use crate::server::state::ApiPrefix;
use crate::{hydra_client, load_balancer};
use axum::Router;
use bf_common::errors::BlockfrostError;
Expand All @@ -10,7 +9,6 @@ pub struct IcebreakersManager {
icebreakers_api: Arc<IcebreakersAPI>,
health_errors: Arc<Mutex<Vec<BlockfrostError>>>,
app: Router,
api_prefix: ApiPrefix,
max_response_body_bytes: usize,
}

Expand All @@ -19,14 +17,12 @@ impl IcebreakersManager {
icebreakers_api: Arc<IcebreakersAPI>,
health_errors: Arc<Mutex<Vec<BlockfrostError>>>,
app: Router,
api_prefix: ApiPrefix,
max_response_body_bytes: usize,
) -> Self {
Self {
icebreakers_api,
health_errors,
app,
api_prefix,
max_response_body_bytes,
}
}
Expand Down Expand Up @@ -57,7 +53,6 @@ impl IcebreakersManager {
tokio::spawn(load_balancer::run_all(
self.app,
self.health_errors,
self.api_prefix,
Some(mutable_hydra_kex),
self.icebreakers_api,
self.max_response_body_bytes,
Expand Down
Loading
Loading