Skip to content

Commit 8e59361

Browse files
authored
Add support for multiple server urls (#13)
1 parent 5ee10f2 commit 8e59361

File tree

6 files changed

+39
-14
lines changed

6 files changed

+39
-14
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2021"
77
anyhow = "1.0.98"
88
borsh = "0.9.3"
99
clap = { version = "4.5.39", features = ["derive", "env"] }
10+
futures = "0.3.31"
1011
hex = { version = "0.4.3", features = ["serde"] }
1112
prost = "0.14.1"
1213
reqwest = { version = "0.12.19", features = ["json"] }

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ Make sure to set `RUST_LOG=INFO` to enable logs from tracing:
2828
```bash
2929
RUST_LOG=INFO cargo run -- run \
3030
--pythnet-url wss://api2.pythnet.pyth.network \
31-
--server-url https://quorum.pyth.network \
31+
--server-url https://quorum-1.pyth.network \
32+
--server-url https://quorum-2.pyth.network \
33+
--server-url https://quorum-3.pyth.network \
3234
--secret-key /path/to/secret.key \
3335
--wormhole-pid H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU
3436
```
3537

38+
You can specify multiple `--server-url` flags to broadcast observations to more than one server.
39+
40+
3641
---
3742

3843
### 🌱 Environment Variables (Optional)
@@ -41,14 +46,16 @@ Instead of CLI flags, you can also set environment variables:
4146

4247
```bash
4348
export PYTHNET_URL=wss://api2.pythnet.pyth.network
44-
export SERVER_URL=https://quorum.pyth.network
49+
export SERVER_URL=https://quorum-1.pyth.network,https://quorum-2.pyth.network,https://quorum-3.pyth.network
4550
export SECRET_KEY=/path/to/secret.key
4651
export WORMHOLE_PID=H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU
4752
export RUST_LOG=INFO
4853

4954
cargo run
5055
```
5156

57+
You can provide multiple server URLs in the `SERVER_URL` environment variable by separating them with commas.
58+
5259
---
5360

5461
### 🔑 Generate a Secret Key

src/api_client.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ impl<P: Serialize> Observation<P> {
5555
}
5656

5757
impl ApiClient {
58+
pub fn get_base_url(&self) -> &Url {
59+
&self.inner.base_url
60+
}
61+
5862
pub fn try_new(
5963
base_url: String,
6064
config: Option<ApiClientConfig>,

src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ pub struct RunOptions {
2121
default_value = "H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU"
2222
)]
2323
pub wormhole_pid: String,
24-
#[arg(long = "server-url", env = "SERVER_URL")]
25-
pub server_url: String,
24+
#[arg(long = "server-url", env = "SERVER_URL", value_delimiter = ',')]
25+
pub server_urls: Vec<String>,
2626
#[arg(long = "mode", env = "MODE", default_value = "production")]
2727
pub mode: Mode,
2828
}

src/main.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
api_client::{ApiClient, Observation},
77
borsh::BorshDeserialize,
88
clap::Parser,
9+
futures::future::join_all,
910
posted_message::PostedMessageUnreliableData,
1011
prost::Message,
1112
secp256k1::{rand::rngs::OsRng, Secp256k1},
@@ -41,7 +42,7 @@ struct RunListenerInput<T: Signer> {
4142
signer: T,
4243
wormhole_pid: Pubkey,
4344
accumulator_address: Pubkey,
44-
api_client: ApiClient,
45+
api_clients: Vec<ApiClient>,
4546
}
4647

4748
fn find_message_pda(wormhole_pid: &Pubkey, slot: u64) -> Pubkey {
@@ -148,16 +149,22 @@ async fn run_listener<T: Signer + 'static>(
148149
};
149150

150151
tokio::spawn({
151-
let (api_client, signer) = (input.api_client.clone(), input.signer.clone());
152+
let (api_clients, signer) = (input.api_clients.clone(), input.signer.clone());
152153
async move {
153154
let body = message_data_to_body(&unreliable_data);
154155
match Observation::try_new(body.clone(), signer.clone()) {
155156
Ok(observation) => {
156-
if let Err(e) = api_client.post_observation(observation).await {
157-
tracing::error!(error = ?e, "Failed to post observation");
158-
} else {
159-
tracing::info!("Observation posted successfully");
160-
};
157+
join_all(api_clients.iter().map(|api_client| {
158+
let observation = observation.clone();
159+
let api_client = api_client.clone();
160+
async move {
161+
if let Err(e) = api_client.post_observation(observation).await {
162+
tracing::warn!(url = api_client.get_base_url().to_string(), error = ?e, "Failed to post observation");
163+
} else {
164+
tracing::info!(url = api_client.get_base_url().to_string(), "Observation posted successfully");
165+
}
166+
}
167+
})).await;
161168
}
162169
Err(e) => tracing::error!(error = ?e, "Failed to create observation"),
163170
}
@@ -182,8 +189,13 @@ async fn run(run_options: config::RunOptions) {
182189
.expect("Invalid accumulator address");
183190
let wormhole_pid =
184191
Pubkey::from_str(&run_options.wormhole_pid).expect("Invalid Wormhole program ID");
185-
let api_client =
186-
ApiClient::try_new(run_options.server_url, None).expect("Failed to create API client");
192+
let api_clients: Vec<ApiClient> = run_options
193+
.server_urls
194+
.into_iter()
195+
.map(|server_url| {
196+
ApiClient::try_new(server_url, None).expect("Failed to create API client")
197+
})
198+
.collect();
187199

188200
let (pubkey, pubkey_evm) = signer.get_public_key().expect("Failed to get public key");
189201
let evm_encded_public_key = format!("0x{}", hex::encode(pubkey_evm));
@@ -199,7 +211,7 @@ async fn run(run_options: config::RunOptions) {
199211
signer: signer.clone(),
200212
wormhole_pid,
201213
accumulator_address,
202-
api_client: api_client.clone(),
214+
api_clients: api_clients.clone(),
203215
})
204216
.await
205217
{

0 commit comments

Comments
 (0)