Skip to content

Commit bb3a535

Browse files
committed
Add support for multiple server urls
1 parent 5ee10f2 commit bb3a535

File tree

4 files changed

+43
-21
lines changed

4 files changed

+43
-21
lines changed

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-green.pyth.network \
32+
--server-url https://quorum-yellow.pyth.network \
33+
--server-url https://quorum-cyan.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-green.pyth.network,https://quorum-yellow.pyth.network,https://quorum-cyan.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: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct RunListenerInput<T: Signer> {
4141
signer: T,
4242
wormhole_pid: Pubkey,
4343
accumulator_address: Pubkey,
44-
api_client: ApiClient,
44+
api_clients: Vec<ApiClient>,
4545
}
4646

4747
fn find_message_pda(wormhole_pid: &Pubkey, slot: u64) -> Pubkey {
@@ -147,21 +147,27 @@ async fn run_listener<T: Signer + 'static>(
147147
Err(_) => continue,
148148
};
149149

150-
tokio::spawn({
151-
let (api_client, signer) = (input.api_client.clone(), input.signer.clone());
152-
async move {
153-
let body = message_data_to_body(&unreliable_data);
154-
match Observation::try_new(body.clone(), signer.clone()) {
155-
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-
};
150+
input.api_clients.iter().for_each(|api_client| {
151+
tokio::spawn({
152+
let (unreliable_data, api_client, signer) = (
153+
unreliable_data.clone(),
154+
api_client.clone(),
155+
input.signer.clone()
156+
);
157+
async move {
158+
let body = message_data_to_body(&unreliable_data);
159+
match Observation::try_new(body.clone(), signer.clone()) {
160+
Ok(observation) => {
161+
if let Err(e) = api_client.post_observation(observation).await {
162+
tracing::error!(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+
Err(e) => tracing::error!(error = ?e, "Failed to create observation"),
161168
}
162-
Err(e) => tracing::error!(error = ?e, "Failed to create observation"),
163169
}
164-
}
170+
});
165171
});
166172
}
167173

@@ -182,8 +188,13 @@ async fn run(run_options: config::RunOptions) {
182188
.expect("Invalid accumulator address");
183189
let wormhole_pid =
184190
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");
191+
let api_clients: Vec<ApiClient> = run_options
192+
.server_urls
193+
.into_iter()
194+
.map(|server_url| {
195+
ApiClient::try_new(server_url, None).expect("Failed to create API client")
196+
})
197+
.collect();
187198

188199
let (pubkey, pubkey_evm) = signer.get_public_key().expect("Failed to get public key");
189200
let evm_encded_public_key = format!("0x{}", hex::encode(pubkey_evm));
@@ -199,7 +210,7 @@ async fn run(run_options: config::RunOptions) {
199210
signer: signer.clone(),
200211
wormhole_pid,
201212
accumulator_address,
202-
api_client: api_client.clone(),
213+
api_clients: api_clients.clone(),
203214
})
204215
.await
205216
{

0 commit comments

Comments
 (0)