Skip to content

Commit a8472d0

Browse files
committed
fix: don't ser/de the binary message, leave that to the client. only b64 encode. add parse and verify to the example.
1 parent d8fba66 commit a8472d0

File tree

4 files changed

+93
-13
lines changed

4 files changed

+93
-13
lines changed

lazer/Cargo.lock

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

lazer/sdk/rust/client/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ base64 = "0.22.1"
1616
anyhow = "1.0"
1717
tracing = "0.1"
1818
url = "2.4"
19+
20+
[dev_dependencies]
21+
bincode = "1.3.3"
22+
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
23+
hex = "0.4.3"
24+
libsecp256k1 = "0.7.1"
25+
bs58 = "0.5.1"
26+
alloy-primitives = "0.8.19"

lazer/sdk/rust/client/examples/subscribe_price_feeds.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use base64::Engine;
12
use futures_util::StreamExt;
23
use pyth_lazer_client::LazerClient;
4+
use pyth_lazer_protocol::message::{EvmMessage, SolanaMessage};
5+
use pyth_lazer_protocol::payload::PayloadData;
36
use pyth_lazer_protocol::router::{
47
Chain, Channel, DeliveryFormat, FixedRate, JsonBinaryEncoding, PriceFeedId, PriceFeedProperty,
58
SubscriptionParams, SubscriptionParamsRepr,
69
};
7-
use pyth_lazer_protocol::subscription::{Request, SubscribeRequest, SubscriptionId};
10+
use pyth_lazer_protocol::subscription::{Request, Response, SubscribeRequest, SubscriptionId};
811

912
fn get_lazer_access_token() -> String {
1013
// Place your access token in your env at LAZER_ACCESS_TOKEN or set it here
@@ -75,7 +78,44 @@ async fn main() -> anyhow::Result<()> {
7578
// Process the first few updates
7679
let mut count = 0;
7780
while let Some(msg) = stream.next().await {
78-
println!("Received update: {:?}", msg?);
81+
// The stream gives us base64-encoded binary messages. We need to decode, parse, and verify them.
82+
match msg? {
83+
Response::StreamUpdated(update) => {
84+
if let Some(evm_data) = update.payload.evm {
85+
// Decode binary data
86+
let binary_data =
87+
base64::engine::general_purpose::STANDARD.decode(&evm_data.data)?;
88+
let evm_message = EvmMessage::deserialize_slice(&binary_data)?;
89+
90+
// Parse and verify the EVM message
91+
let payload = parse_and_verify_evm_message(&evm_message);
92+
println!("EVM payload: {payload:?}\n");
93+
}
94+
95+
if let Some(solana_data) = update.payload.solana {
96+
// Decode binary data
97+
let binary_data =
98+
base64::engine::general_purpose::STANDARD.decode(&solana_data.data)?;
99+
let solana_message = SolanaMessage::deserialize_slice(&binary_data)?;
100+
101+
// Parse and verify the Solana message
102+
let payload = parse_and_verify_solana_message(&solana_message);
103+
println!("Solana payload: {payload:?}\n");
104+
}
105+
106+
if let Some(parsed) = update.payload.parsed {
107+
// Parsed payloads (`parsed: true`) are already decoded and ready to use
108+
for feed in parsed.price_feeds {
109+
println!(
110+
"Parsed payload: {:?}: {:?} at {:?}\n",
111+
feed.price_feed_id, feed, parsed.timestamp_us
112+
);
113+
}
114+
}
115+
}
116+
_ => println!("Received non-update message"),
117+
}
118+
79119
count += 1;
80120
if count >= 50 {
81121
break;
@@ -92,3 +132,27 @@ async fn main() -> anyhow::Result<()> {
92132
client.close().await?;
93133
Ok(())
94134
}
135+
136+
fn parse_and_verify_solana_message(solana_message: &SolanaMessage) -> anyhow::Result<PayloadData> {
137+
// Verify signature using the pubkey
138+
let public_key = ed25519_dalek::VerifyingKey::from_bytes(&solana_message.public_key)?;
139+
public_key.verify_strict(
140+
&solana_message.payload,
141+
&ed25519_dalek::Signature::from_bytes(&solana_message.signature),
142+
)?;
143+
144+
let payload = PayloadData::deserialize_slice_le(&solana_message.payload)?;
145+
Ok(payload)
146+
}
147+
148+
fn parse_and_verify_evm_message(evm_message: &EvmMessage) -> anyhow::Result<PayloadData> {
149+
// Recover pubkey from message
150+
libsecp256k1::recover(
151+
&libsecp256k1::Message::parse(&alloy_primitives::keccak256(&evm_message.payload)),
152+
&libsecp256k1::Signature::parse_standard(&evm_message.signature)?,
153+
&libsecp256k1::RecoveryId::parse(evm_message.recovery_id)?,
154+
)?;
155+
156+
let payload = PayloadData::deserialize_slice_be(&evm_message.payload)?;
157+
Ok(payload)
158+
}

lazer/sdk/rust/protocol/src/subscription.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
44
use {
55
crate::{
6-
message::{EvmMessage, SolanaMessage},
76
payload::{
87
BINARY_UPDATE_FORMAT_MAGIC, EVM_FORMAT_MAGIC, PARSED_FORMAT_MAGIC,
98
SOLANA_FORMAT_MAGIC_BE,
@@ -79,10 +78,18 @@ impl Response {
7978

8079
match magic {
8180
EVM_FORMAT_MAGIC => {
82-
evm = Some(EvmMessage::deserialize_slice(&data[pos..pos + len])?);
81+
evm = Some(JsonBinaryData {
82+
encoding: JsonBinaryEncoding::Base64,
83+
data: base64::engine::general_purpose::STANDARD
84+
.encode(&data[pos..pos + len]),
85+
});
8386
}
8487
SOLANA_FORMAT_MAGIC_BE => {
85-
solana = Some(SolanaMessage::deserialize_slice(&data[pos..pos + len])?);
88+
solana = Some(JsonBinaryData {
89+
encoding: JsonBinaryEncoding::Base64,
90+
data: base64::engine::general_purpose::STANDARD
91+
.encode(&data[pos..pos + len]),
92+
});
8693
}
8794
PARSED_FORMAT_MAGIC => {
8895
parsed = Some(serde_json::from_slice(&data[pos + 4..pos + len])?);
@@ -95,19 +102,14 @@ impl Response {
95102
Ok(Response::StreamUpdated(StreamUpdatedResponse {
96103
subscription_id,
97104
payload: JsonUpdate {
98-
evm: evm.map(|m| JsonBinaryData {
99-
encoding: JsonBinaryEncoding::Base64,
100-
data: base64::engine::general_purpose::STANDARD.encode(&m.payload),
101-
}),
102-
solana: solana.map(|m| JsonBinaryData {
103-
encoding: JsonBinaryEncoding::Base64,
104-
data: base64::engine::general_purpose::STANDARD.encode(&m.payload),
105-
}),
105+
evm,
106+
solana,
106107
parsed,
107108
},
108109
}))
109110
}
110111
}
112+
111113
/// Sent from the server after a successul subscription.
112114
#[derive(Debug, Clone, Serialize, Deserialize)]
113115
#[serde(rename_all = "camelCase")]

0 commit comments

Comments
 (0)