Skip to content

Commit ac8cb45

Browse files
committed
fix: support notifications, configs, watchers
1 parent 33f8dc5 commit ac8cb45

File tree

7 files changed

+361
-225
lines changed

7 files changed

+361
-225
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2023-, Edge & Node, GraphOps, and Semiotic Labs.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Horizon (V2) contract detection utilities
5+
//!
6+
//! This module provides functionality to detect if Horizon (V2) contracts are active
7+
//! in the network by querying the network subgraph for PaymentsEscrow accounts.
8+
9+
use anyhow::Result;
10+
use indexer_query::horizon_detection::{self, HorizonDetectionQuery};
11+
12+
use crate::client::SubgraphClient;
13+
14+
/// Detects if Horizon (V2) contracts are active in the network.
15+
///
16+
/// This function queries the network subgraph to check if any PaymentsEscrow accounts exist.
17+
/// If they do, it indicates that Horizon contracts are deployed and active.
18+
///
19+
/// # Arguments
20+
/// * `network_subgraph` - The network subgraph client to query
21+
///
22+
/// # Returns
23+
/// * `Ok(true)` if Horizon contracts are active
24+
/// * `Ok(false)` if only legacy (V1) contracts are active
25+
/// * `Err(...)` if there was an error querying the subgraph
26+
pub async fn is_horizon_active(network_subgraph: &SubgraphClient) -> Result<bool> {
27+
tracing::debug!("Checking if Horizon (V2) contracts are active in the network");
28+
29+
let response = network_subgraph
30+
.query::<HorizonDetectionQuery, _>(horizon_detection::Variables {})
31+
.await?;
32+
33+
let response = response?;
34+
35+
// If we find any PaymentsEscrow accounts, Horizon is active
36+
let horizon_active = !response.payments_escrow_accounts.is_empty();
37+
38+
if horizon_active {
39+
tracing::info!(
40+
"Horizon (V2) contracts detected - found {} PaymentsEscrow accounts",
41+
response.payments_escrow_accounts.len()
42+
);
43+
} else {
44+
tracing::info!("No Horizon (V2) contracts found - using legacy (V1) mode");
45+
}
46+
47+
Ok(horizon_active)
48+
}

crates/monitor/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod client;
77
mod deployment_to_allocation;
88
mod dispute_manager;
99
mod escrow_accounts;
10+
mod horizon_detection;
1011

1112
pub use crate::{
1213
allocations::{indexer_allocations, AllocationWatcher},
@@ -18,4 +19,5 @@ pub use crate::{
1819
empty_escrow_accounts_watcher, escrow_accounts_v1, escrow_accounts_v2, EscrowAccounts,
1920
EscrowAccountsError, EscrowAccountsWatcher,
2021
},
22+
horizon_detection::is_horizon_active,
2123
};

crates/service/src/service.rs

Lines changed: 118 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -105,144 +105,127 @@ pub async fn run() -> anyhow::Result<()> {
105105
// V2 escrow accounts are in the network subgraph
106106
let escrow_v2_query_url_for_dips = Some(config.subgraphs.network.config.query_url.clone());
107107

108-
// Configure router with escrow watchers based on Horizon mode
109-
use indexer_config::HorizonMode;
110-
let router = match config.horizon.mode {
111-
HorizonMode::Legacy => {
112-
tracing::info!("Horizon mode: Legacy - using escrow accounts v1 only");
113-
// Only create v1 watcher for legacy mode
114-
let escrow_subgraph_v1 = create_subgraph_client(
115-
http_client.clone(),
116-
&config.graph_node,
117-
&config.subgraphs.escrow.config,
118-
)
119-
.await;
120-
121-
let v1_watcher = indexer_monitor::escrow_accounts_v1(
122-
escrow_subgraph_v1,
123-
indexer_address,
124-
config.subgraphs.escrow.config.syncing_interval_secs,
125-
true, // Reject thawing signers eagerly
126-
)
127-
.await
128-
.expect("Error creating escrow_accounts_v1 channel");
129-
130-
ServiceRouter::builder()
131-
.database(database.clone())
132-
.domain_separator(domain_separator.clone())
133-
.graph_node(config.graph_node)
134-
.http_client(http_client)
135-
.release(release)
136-
.indexer(config.indexer)
137-
.service(config.service)
138-
.blockchain(config.blockchain)
139-
.timestamp_buffer_secs(config.tap.rav_request.timestamp_buffer_secs)
140-
.network_subgraph(network_subgraph, config.subgraphs.network)
141-
.escrow_accounts_v1(v1_watcher)
142-
.build()
108+
// Determine if we should check for Horizon contracts and potentially enable hybrid mode:
109+
// - If horizon.enabled = false: Pure legacy mode, no Horizon detection
110+
// - If horizon.enabled = true: Check if Horizon contracts are active in the network
111+
let is_horizon_active = if config.horizon.enabled {
112+
tracing::info!("Horizon migration support enabled - checking if Horizon contracts are active in the network");
113+
match indexer_monitor::is_horizon_active(network_subgraph).await {
114+
Ok(active) => {
115+
if active {
116+
tracing::info!("Horizon contracts detected in network subgraph - enabling hybrid migration mode");
117+
tracing::info!("Mode: Accept new V2 receipts only, continue processing existing V1 receipts for RAVs");
118+
} else {
119+
tracing::info!("Horizon contracts not yet active in network subgraph - remaining in legacy mode");
120+
}
121+
active
122+
}
123+
Err(e) => {
124+
tracing::warn!(
125+
"Failed to detect Horizon contracts: {}. Remaining in legacy mode.",
126+
e
127+
);
128+
false
129+
}
143130
}
144-
HorizonMode::Transition => {
145-
tracing::info!("Horizon mode: Transition - using both escrow accounts v1 and v2");
146-
// Create both watchers for transition mode
147-
let escrow_subgraph_v1 = create_subgraph_client(
148-
http_client.clone(),
149-
&config.graph_node,
150-
&config.subgraphs.escrow.config,
151-
)
152-
.await;
131+
} else {
132+
tracing::info!(
133+
"Horizon migration support disabled in configuration - using pure legacy mode"
134+
);
135+
false
136+
};
153137

154-
let v1_watcher = indexer_monitor::escrow_accounts_v1(
155-
escrow_subgraph_v1,
156-
indexer_address,
157-
config.subgraphs.escrow.config.syncing_interval_secs,
158-
true, // Reject thawing signers eagerly
159-
)
160-
.await
161-
.expect("Error creating escrow_accounts_v1 channel");
138+
// Configure router with escrow watchers based on automatic Horizon detection
139+
let router = if is_horizon_active {
140+
tracing::info!("Horizon contracts detected - using Horizon migration mode: V2 receipts only, but processing existing V1 receipts");
162141

163-
// V2 escrow accounts are in the network subgraph
164-
let v2_watcher = match indexer_monitor::escrow_accounts_v2(
165-
network_subgraph,
166-
indexer_address,
167-
config.subgraphs.network.config.syncing_interval_secs,
168-
true, // Reject thawing signers eagerly
169-
)
170-
.await
171-
{
172-
Ok(watcher) => {
173-
tracing::info!(
174-
"V2 escrow accounts successfully initialized from network subgraph"
175-
);
176-
watcher
177-
}
178-
Err(e) => {
179-
tracing::warn!(
180-
"V2 escrow accounts temporarily disabled - network subgraph query failed: {}. \
181-
Indexer will only process V1 TAP receipts until this is fixed.",
182-
e
183-
);
184-
// Create empty watcher as fallback
185-
indexer_monitor::empty_escrow_accounts_watcher()
186-
}
187-
};
188-
189-
ServiceRouter::builder()
190-
.database(database.clone())
191-
.domain_separator(domain_separator.clone())
192-
.graph_node(config.graph_node)
193-
.http_client(http_client)
194-
.release(release)
195-
.indexer(config.indexer)
196-
.service(config.service)
197-
.blockchain(config.blockchain)
198-
.timestamp_buffer_secs(config.tap.rav_request.timestamp_buffer_secs)
199-
.network_subgraph(network_subgraph, config.subgraphs.network)
200-
.escrow_accounts_v1(v1_watcher)
201-
.escrow_accounts_v2(v2_watcher)
202-
.build()
203-
}
204-
HorizonMode::Full => {
205-
tracing::info!("Horizon mode: Full - using escrow accounts v2 only");
206-
// V2 escrow accounts are in the network subgraph
207-
let v2_watcher = match indexer_monitor::escrow_accounts_v2(
208-
network_subgraph,
209-
indexer_address,
210-
config.subgraphs.network.config.syncing_interval_secs,
211-
true, // Reject thawing signers eagerly
212-
)
213-
.await
214-
{
215-
Ok(watcher) => {
216-
tracing::info!(
217-
"V2 escrow accounts successfully initialized from network subgraph"
218-
);
219-
watcher
220-
}
221-
Err(e) => {
222-
tracing::warn!(
223-
"V2 escrow accounts temporarily disabled - network subgraph query failed: {}. \
224-
Indexer will only process V1 TAP receipts until this is fixed.",
225-
e
226-
);
227-
// Create empty watcher as fallback
228-
indexer_monitor::empty_escrow_accounts_watcher()
229-
}
230-
};
231-
232-
ServiceRouter::builder()
233-
.database(database.clone())
234-
.domain_separator(domain_separator.clone())
235-
.graph_node(config.graph_node)
236-
.http_client(http_client)
237-
.release(release)
238-
.indexer(config.indexer)
239-
.service(config.service)
240-
.blockchain(config.blockchain)
241-
.timestamp_buffer_secs(config.tap.rav_request.timestamp_buffer_secs)
242-
.network_subgraph(network_subgraph, config.subgraphs.network)
243-
.escrow_accounts_v2(v2_watcher)
244-
.build()
245-
}
142+
// Create V1 escrow watcher for processing existing receipts
143+
let escrow_subgraph_v1 = create_subgraph_client(
144+
http_client.clone(),
145+
&config.graph_node,
146+
&config.subgraphs.escrow.config,
147+
)
148+
.await;
149+
150+
let v1_watcher = indexer_monitor::escrow_accounts_v1(
151+
escrow_subgraph_v1,
152+
indexer_address,
153+
config.subgraphs.escrow.config.syncing_interval_secs,
154+
true, // Reject thawing signers eagerly
155+
)
156+
.await
157+
.expect("Error creating escrow_accounts_v1 channel");
158+
159+
// Create V2 escrow watcher for new receipts (V2 escrow accounts are in the network subgraph)
160+
let v2_watcher = match indexer_monitor::escrow_accounts_v2(
161+
network_subgraph,
162+
indexer_address,
163+
config.subgraphs.network.config.syncing_interval_secs,
164+
true, // Reject thawing signers eagerly
165+
)
166+
.await
167+
{
168+
Ok(watcher) => {
169+
tracing::info!("V2 escrow accounts successfully initialized from network subgraph");
170+
watcher
171+
}
172+
Err(e) => {
173+
tracing::error!(
174+
"Failed to initialize V2 escrow accounts: {}. Service cannot continue.",
175+
e
176+
);
177+
std::process::exit(1);
178+
}
179+
};
180+
181+
ServiceRouter::builder()
182+
.database(database.clone())
183+
.domain_separator(domain_separator.clone())
184+
.graph_node(config.graph_node)
185+
.http_client(http_client)
186+
.release(release)
187+
.indexer(config.indexer)
188+
.service(config.service)
189+
.blockchain(config.blockchain)
190+
.timestamp_buffer_secs(config.tap.rav_request.timestamp_buffer_secs)
191+
.network_subgraph(network_subgraph, config.subgraphs.network)
192+
.escrow_accounts_v1(v1_watcher)
193+
.escrow_accounts_v2(v2_watcher)
194+
.build()
195+
} else {
196+
tracing::info!(
197+
"No Horizon contracts detected - using Legacy (V1) mode with escrow accounts v1 only"
198+
);
199+
// Only create v1 watcher for legacy mode
200+
let escrow_subgraph_v1 = create_subgraph_client(
201+
http_client.clone(),
202+
&config.graph_node,
203+
&config.subgraphs.escrow.config,
204+
)
205+
.await;
206+
207+
let v1_watcher = indexer_monitor::escrow_accounts_v1(
208+
escrow_subgraph_v1,
209+
indexer_address,
210+
config.subgraphs.escrow.config.syncing_interval_secs,
211+
true, // Reject thawing signers eagerly
212+
)
213+
.await
214+
.expect("Error creating escrow_accounts_v1 channel");
215+
216+
ServiceRouter::builder()
217+
.database(database.clone())
218+
.domain_separator(domain_separator.clone())
219+
.graph_node(config.graph_node)
220+
.http_client(http_client)
221+
.release(release)
222+
.indexer(config.indexer)
223+
.service(config.service)
224+
.blockchain(config.blockchain)
225+
.timestamp_buffer_secs(config.tap.rav_request.timestamp_buffer_secs)
226+
.network_subgraph(network_subgraph, config.subgraphs.network)
227+
.escrow_accounts_v1(v1_watcher)
228+
.build()
246229
};
247230

248231
serve_metrics(config.metrics.get_socket_addr());

0 commit comments

Comments
 (0)