From 2abba81d6b78870c78aec9e5d6ec6fb15dadd73a Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 8 May 2025 16:32:38 +0200 Subject: [PATCH 01/11] fix: add build-essential to docker --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 08cc7132..173a78f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM rust:slim-bookworm as builder -RUN apt update && apt install -y curl libssl-dev pkg-config && apt clean all +RUN apt update && apt install -y curl libssl-dev pkg-config build-essential && apt clean all ADD . /agent WORKDIR /agent From fdcfe1558fda0f9699d48c4c5429756bde562084 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 8 May 2025 16:33:12 +0200 Subject: [PATCH 02/11] fix: change wss_url to wss_urls --- config/config.sample.pythnet.toml | 2 +- config/config.sample.pythtest.toml | 2 +- config/config.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.sample.pythnet.toml b/config/config.sample.pythnet.toml index d5ffbcad..870cb16c 100644 --- a/config/config.sample.pythnet.toml +++ b/config/config.sample.pythnet.toml @@ -10,7 +10,7 @@ rpc_urls = ["https://api2.pythnet.pyth.network"] # WS(S) endpoint of the RRC node. This is used to subscribe to account changes on the network. # This can be omitted when oracle.subscriber_enabled is set to false. -wss_url = "wss://api2.pythnet.pyth.network" +wss_urls = ["wss://api2.pythnet.pyth.network"] # Path to your publishing keypair. key_store.publish_keypair_path = "/path/to/keypair.json" diff --git a/config/config.sample.pythtest.toml b/config/config.sample.pythtest.toml index 68906608..bf5f9157 100644 --- a/config/config.sample.pythtest.toml +++ b/config/config.sample.pythtest.toml @@ -10,7 +10,7 @@ rpc_urls = ["https://api.pythtest.pyth.network"] # WS(S) endpoint of the RRC node. This is used to subscribe to account changes # on the network. This can be omitted when oracle.subscriber_enabled is set to # false. -wss_url = "wss://api.pythtest.pyth.network" +wss_urls = ["wss://api.pythtest.pyth.network"] # Path to your publishing keypair. key_store.publish_keypair_path = "/path/to/keypair.json" diff --git a/config/config.toml b/config/config.toml index 6cf270db..b7164451 100644 --- a/config/config.toml +++ b/config/config.toml @@ -37,7 +37,7 @@ rpc_urls = ["https://api.pythtest.pyth.network"] # WS(S) endpoint of the RRC node. This is used to subscribe to account changes on the network. # This can be omitted when oracle.subscriber_enabled is set to false. -wss_url = "wss://api.pythtest.pyth.network" +wss_urls = ["wss://api.pythtest.pyth.network"] # Path to the keypair used to publish price updates. If set to a # non-existent file path, the system expects a keypair to be loaded From 366a1e5b340aceeffbb015da70d40c13a6c0684d Mon Sep 17 00:00:00 2001 From: Mike Rolish Date: Mon, 12 May 2025 00:28:58 -0500 Subject: [PATCH 03/11] Bump solana cli install url and version --- integration-tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/Dockerfile b/integration-tests/Dockerfile index d674e641..476850d0 100644 --- a/integration-tests/Dockerfile +++ b/integration-tests/Dockerfile @@ -12,7 +12,7 @@ ENV PATH="${PATH}:/root/.local/bin" RUN poetry config virtualenvs.in-project true # Install Solana Tool Suite -RUN sh -c "$(curl -sSfL https://release.solana.com/v1.14.17/install)" +RUN sh -c "$(curl -sSfL https://release.anza.xyz/v2.2.1/install)" ENV PATH="${PATH}:/root/.local/share/solana/install/active_release/bin" ADD . /agent From c414a838d2ec27a3acc8996be36f8d74d27c673d Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 10:16:34 +0200 Subject: [PATCH 04/11] chore: bump version as a major change --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0db79f1f..d1c22d2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3564,7 +3564,7 @@ dependencies = [ [[package]] name = "pyth-agent" -version = "2.12.3" +version = "3.0.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 5ccefead..84c94fff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-agent" -version = "2.12.3" +version = "3.0.0" edition = "2024" [[bin]] From bbf014e5a836f1a300584c220eab1b80938944cb Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 10:23:42 +0200 Subject: [PATCH 05/11] chore: add docs for lazer config --- config/config.toml | 21 +++++++++++++++++++++ src/agent/services/lazer_exporter.rs | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/config/config.toml b/config/config.toml index b7164451..6773e521 100644 --- a/config/config.toml +++ b/config/config.toml @@ -218,3 +218,24 @@ exporter_timeout_duration = "3s" # Endpoint URL for the OpenTelemetry exporter exporter_endpoint = "http://127.0.0.1:4317" + +## Configuration for Pyth Lazer ## + +# [pyth_lazer] +# URL for the history service +# history_url = "https://pyth-lazer-staging.dourolabs.app/history/" + +# URLs for the Lazer relayers to connect to +# relayer_urls = ["wss://pyth-lazer-staging.dourolabs.app/v2/publisher"] + +# Unique identifier for this publisher +# publisher_id = 1 + +# Authorization token for connecting to relayers +# authorization_token = "your-auth-token" + +# Path to the publisher's secret key file +# publisher_secret_key = "/path/to/publisher-key.json" + +# Duration between price updates (defaults to 200ms if not specified) +# publish_interval_duration = "200ms" diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 6082d9b0..3d9f4d5d 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -62,7 +62,7 @@ impl std::fmt::Debug for PublisherSecretKey { } fn default_publish_interval() -> Duration { - Duration::from_millis(10) + Duration::from_millis(200) } struct RelayerSender { From 142ec9c524efd97d7cdd01a727b52da07d0fe0f4 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 10:30:54 +0200 Subject: [PATCH 06/11] refactor: move routes out of the config to the code --- config/config.toml | 4 ++-- src/agent/services/lazer_exporter.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/config.toml b/config/config.toml index 6773e521..1d0d43bc 100644 --- a/config/config.toml +++ b/config/config.toml @@ -223,10 +223,10 @@ exporter_endpoint = "http://127.0.0.1:4317" # [pyth_lazer] # URL for the history service -# history_url = "https://pyth-lazer-staging.dourolabs.app/history/" +# history_url = "https://pyth-lazer-staging.dourolabs.app" # URLs for the Lazer relayers to connect to -# relayer_urls = ["wss://pyth-lazer-staging.dourolabs.app/v2/publisher"] +# relayer_urls = ["wss://pyth-lazer-staging.dourolabs.app"] # Unique identifier for this publisher # publisher_id = 1 diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 3d9f4d5d..fcf81eff 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -85,13 +85,14 @@ impl RelayerSender { } async fn connect_to_relayer( - url: &Url, + mut url: Url, token: &str, ) -> Result<( SplitSink>, TungsteniteMessage>, SplitStream>>, )> { tracing::info!("connecting to the relayer at {}", url); + url.set_path("/v2/publisher"); let mut req = url.clone().into_client_request()?; let headers = req.headers_mut(); headers.insert( @@ -112,7 +113,7 @@ async fn connect_to_relayers( let mut relayer_receivers = Vec::new(); for url in config.relayer_urls.clone() { let (relayer_sender, relayer_receiver) = - connect_to_relayer(&url, &config.authorization_token).await?; + connect_to_relayer(url, &config.authorization_token).await?; relayer_senders.push(relayer_sender); relayer_receivers.push(relayer_receiver); } From 9929efec7cb62e66b000de66f5deccad4d578444 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 10:48:13 +0200 Subject: [PATCH 07/11] refactor: read key from file --- config/config.toml | 2 +- src/agent/services/lazer_exporter.rs | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/config/config.toml b/config/config.toml index 1d0d43bc..b232e3c4 100644 --- a/config/config.toml +++ b/config/config.toml @@ -235,7 +235,7 @@ exporter_endpoint = "http://127.0.0.1:4317" # authorization_token = "your-auth-token" # Path to the publisher's secret key file -# publisher_secret_key = "/path/to/publisher-key.json" +# publish_keypair_path = "/path/to/publisher-key.json" # Duration between price updates (defaults to 200ms if not specified) # publish_interval_duration = "200ms" diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index fcf81eff..b766c388 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -19,6 +19,7 @@ use { reqwest::Client, serde::Deserialize, std::{ + path::PathBuf, sync::Arc, time::Duration, }, @@ -48,7 +49,7 @@ pub struct Config { pub relayer_urls: Vec, pub publisher_id: u32, pub authorization_token: String, - publisher_secret_key: PublisherSecretKey, + pub publish_keypair_path: PathBuf, #[serde(with = "humantime_serde", default = "default_publish_interval")] pub publish_interval_duration: Duration, } @@ -173,7 +174,10 @@ mod lazer_exporter { }, state::local::LocalStore, }, - anyhow::bail, + anyhow::{ + Context, + bail, + }, ed25519_dalek::{ Signer, SigningKey, @@ -198,6 +202,7 @@ mod lazer_exporter { lazer_transaction::Payload, }, }, + solana_sdk::signer::keypair, std::{ collections::HashMap, sync::Arc, @@ -260,7 +265,21 @@ mod lazer_exporter { stream_map.insert(config.relayer_urls[i].clone(), receiver); } - let signing_key = SigningKey::from_bytes(&config.publisher_secret_key.0); + // Read the keypair from the file using Solana SDK because it's the same key used by the Pythnet publisher + let publish_keypair = match keypair::read_keypair_file(&config.publish_keypair_path) { + Ok(k) => k, + Err(e) => { + tracing::warn!( + error = ?e, + publish_keypair_path = config.publish_keypair_path.display().to_string(), + "Reading publish keypair returned an error. ", + ); + bail!("Reading publish keypair returned an error. "); + } + }; + + let signing_key = SigningKey::from_keypair_bytes(&publish_keypair.to_bytes()) + .context("Failed to create signing key from keypair")?; let mut publish_interval = tokio::time::interval(config.publish_interval_duration); loop { From 3918a42f3b431430cd8ada4cd930e55d7762385a Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 10:49:33 +0200 Subject: [PATCH 08/11] fix: use transaction route --- src/agent/services/lazer_exporter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index b766c388..1a286430 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -93,7 +93,7 @@ async fn connect_to_relayer( SplitStream>>, )> { tracing::info!("connecting to the relayer at {}", url); - url.set_path("/v2/publisher"); + url.set_path("/v1/transaction"); let mut req = url.clone().into_client_request()?; let headers = req.headers_mut(); headers.insert( From 9ef75d520e4fdb273bec7fe57e945a17b94f2a84 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 10:50:42 +0200 Subject: [PATCH 09/11] refactor: use err instead of warn on key loading failure --- src/agent/services/lazer_exporter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 1a286430..7e0c6c54 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -269,7 +269,7 @@ mod lazer_exporter { let publish_keypair = match keypair::read_keypair_file(&config.publish_keypair_path) { Ok(k) => k, Err(e) => { - tracing::warn!( + tracing::error!( error = ?e, publish_keypair_path = config.publish_keypair_path.display().to_string(), "Reading publish keypair returned an error. ", From f89b9cd4a6d6ff39ead76c109ac49a3abd657371 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 11:04:51 +0200 Subject: [PATCH 10/11] refactor: remove authorization token as it's not needed anymore --- config/config.toml | 3 --- src/agent/services/lazer_exporter.rs | 10 +--------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/config/config.toml b/config/config.toml index b232e3c4..d6424034 100644 --- a/config/config.toml +++ b/config/config.toml @@ -231,9 +231,6 @@ exporter_endpoint = "http://127.0.0.1:4317" # Unique identifier for this publisher # publisher_id = 1 -# Authorization token for connecting to relayers -# authorization_token = "your-auth-token" - # Path to the publisher's secret key file # publish_keypair_path = "/path/to/publisher-key.json" diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 7e0c6c54..67530da0 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -48,7 +48,6 @@ pub struct Config { pub history_url: Url, pub relayer_urls: Vec, pub publisher_id: u32, - pub authorization_token: String, pub publish_keypair_path: PathBuf, #[serde(with = "humantime_serde", default = "default_publish_interval")] pub publish_interval_duration: Duration, @@ -87,7 +86,6 @@ impl RelayerSender { async fn connect_to_relayer( mut url: Url, - token: &str, ) -> Result<( SplitSink>, TungsteniteMessage>, SplitStream>>, @@ -95,11 +93,6 @@ async fn connect_to_relayer( tracing::info!("connecting to the relayer at {}", url); url.set_path("/v1/transaction"); let mut req = url.clone().into_client_request()?; - let headers = req.headers_mut(); - headers.insert( - "Authorization", - HeaderValue::from_str(&format!("Bearer {}", token))?, - ); let (ws_stream, _) = connect_async_with_config(req, None, true).await?; Ok(ws_stream.split()) } @@ -113,8 +106,7 @@ async fn connect_to_relayers( let mut relayer_senders = Vec::new(); let mut relayer_receivers = Vec::new(); for url in config.relayer_urls.clone() { - let (relayer_sender, relayer_receiver) = - connect_to_relayer(url, &config.authorization_token).await?; + let (relayer_sender, relayer_receiver) = connect_to_relayer(url).await?; relayer_senders.push(relayer_sender); relayer_receivers.push(relayer_receiver); } From 7c5bc21db5bf0e2befb64425a1e647490560a56e Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 12 May 2025 11:17:40 +0200 Subject: [PATCH 11/11] Revert "refactor: remove authorization token as it's not needed anymore" This reverts commit f89b9cd4a6d6ff39ead76c109ac49a3abd657371. --- config/config.toml | 3 +++ src/agent/services/lazer_exporter.rs | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/config/config.toml b/config/config.toml index d6424034..b232e3c4 100644 --- a/config/config.toml +++ b/config/config.toml @@ -231,6 +231,9 @@ exporter_endpoint = "http://127.0.0.1:4317" # Unique identifier for this publisher # publisher_id = 1 +# Authorization token for connecting to relayers +# authorization_token = "your-auth-token" + # Path to the publisher's secret key file # publish_keypair_path = "/path/to/publisher-key.json" diff --git a/src/agent/services/lazer_exporter.rs b/src/agent/services/lazer_exporter.rs index 67530da0..7e0c6c54 100644 --- a/src/agent/services/lazer_exporter.rs +++ b/src/agent/services/lazer_exporter.rs @@ -48,6 +48,7 @@ pub struct Config { pub history_url: Url, pub relayer_urls: Vec, pub publisher_id: u32, + pub authorization_token: String, pub publish_keypair_path: PathBuf, #[serde(with = "humantime_serde", default = "default_publish_interval")] pub publish_interval_duration: Duration, @@ -86,6 +87,7 @@ impl RelayerSender { async fn connect_to_relayer( mut url: Url, + token: &str, ) -> Result<( SplitSink>, TungsteniteMessage>, SplitStream>>, @@ -93,6 +95,11 @@ async fn connect_to_relayer( tracing::info!("connecting to the relayer at {}", url); url.set_path("/v1/transaction"); let mut req = url.clone().into_client_request()?; + let headers = req.headers_mut(); + headers.insert( + "Authorization", + HeaderValue::from_str(&format!("Bearer {}", token))?, + ); let (ws_stream, _) = connect_async_with_config(req, None, true).await?; Ok(ws_stream.split()) } @@ -106,7 +113,8 @@ async fn connect_to_relayers( let mut relayer_senders = Vec::new(); let mut relayer_receivers = Vec::new(); for url in config.relayer_urls.clone() { - let (relayer_sender, relayer_receiver) = connect_to_relayer(url).await?; + let (relayer_sender, relayer_receiver) = + connect_to_relayer(url, &config.authorization_token).await?; relayer_senders.push(relayer_sender); relayer_receivers.push(relayer_receiver); }