Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ semver = "1.0.22"
serde = "1.0.215"
serde_json = "1.0.116"
serde_yaml = "0.9.25"
wincode = { version = "0.2", features = ["std", "alloc", "derive"] }

# Agave Crates
solana-client = "3.0.0"
Expand Down
5 changes: 5 additions & 0 deletions apps/jet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ yellowstone-jet-tpu-client = { workspace = true, features = ["prometheus", "yell
yellowstone-shield-store = { workspace = true }
tokio-util = { workspace = true }
tikv-jemallocator = { workspace = true }
wincode = { workspace = true, optional = true }

[features]
default = []
wincode = ["dep:wincode"]

[build-dependencies]
anyhow = { workspace = true }
Expand Down
5 changes: 5 additions & 0 deletions apps/jet/src/bin/gentx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,12 @@ impl TransactionSender {
if !config.forwarding_policies.is_empty() && !should_use_legacy_txn {
let client = Client::new();

#[cfg(feature = "wincode")]
let tx_bytes =
yellowstone_jet::wincode_schema::serialize_transaction(&transaction)?;
#[cfg(not(feature = "wincode"))]
let tx_bytes = bincode::serialize(&transaction)?;

let encoded_tx = BASE64_STANDARD.encode(tx_bytes);

let payload = serde_json::json!({
Expand Down
48 changes: 43 additions & 5 deletions apps/jet/src/grpc_jet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,24 @@ async fn get_jet_gw_subscribe_auth_token(
let challenge = &challenge_resp.challenge;
let nonce = Uuid::new_v4().as_bytes().to_vec();
let signed_challenge = append_nonce_and_sign(&signer, challenge, &nonce);

#[cfg(feature = "wincode")]
let signature_bytes = {
use crate::wincode_schema::SignatureSchema;
let schema = SignatureSchema::from(signed_challenge);
schema
.to_bytes()
.map_err(|e| anyhow::anyhow!("failed to serialize signed challenge: {e}"))?
};
#[cfg(not(feature = "wincode"))]
let signature_bytes = bincode::serialize(&signed_challenge)
.map_err(|e| anyhow::anyhow!("failed to serialize signed challenge: {e}"))?;

AuthRequest {
auth_step: Some(auth_request::AuthStep::CompleteAuth(
AnswerChallengeRequest {
challenge: challenge_resp.challenge,
signature: bincode::serialize(&signed_challenge)
.expect("failed to serialize signed challenge"),
signature: signature_bytes,
pubkey_to_verify: signer.pubkey().to_bytes().to_vec(),
nonce,
},
Expand All @@ -155,9 +167,20 @@ async fn get_jet_gw_subscribe_auth_token(
if success {
let otak = bs58::decode(one_time_auth_token)
.into_vec()
.expect("failed to decode one-time-auth-token");
.map_err(|e| anyhow::anyhow!("failed to decode one-time-auth-token: {e}"))?;

#[cfg(feature = "wincode")]
let otak = {
use crate::wincode_schema::OneTimeAuthTokenSchema;
let schema = OneTimeAuthTokenSchema::from_bytes(&otak).map_err(|e| {
anyhow::anyhow!("unexpected one-time-auth-token format: {e}")
})?;
OneTimeAuthToken::new(solana_pubkey::Pubkey::from(schema.pubkey), schema.nonce)
};
#[cfg(not(feature = "wincode"))]
let otak = bincode::deserialize::<OneTimeAuthToken>(&otak)
.expect("unexpected one-time-auth-token format");
.map_err(|e| anyhow::anyhow!("unexpected one-time-auth-token format: {e}"))?;

Ok(otak)
} else {
Err(anyhow::anyhow!("failed to authenticate"))
Expand Down Expand Up @@ -212,7 +235,22 @@ pub async fn grpc_subscribe_jet_gw(

// Set up authenticated connection
let mut subscribe_req = Request::new(init_rx);
let ser_otak = bincode::serialize(&otak).expect("failed to serialize one-time-auth-token");

#[cfg(feature = "wincode")]
let ser_otak = {
use crate::wincode_schema::{OneTimeAuthTokenSchema, PubkeySchema};
let schema = OneTimeAuthTokenSchema {
pubkey: PubkeySchema::from(otak.pubkey()),
nonce: otak.nonce().to_vec(),
};
schema
.to_bytes()
.map_err(|e| anyhow::anyhow!("failed to serialize one-time-auth-token: {e}"))?
};
#[cfg(not(feature = "wincode"))]
let ser_otak = bincode::serialize(&otak)
.map_err(|e| anyhow::anyhow!("failed to serialize one-time-auth-token: {e}"))?;

let bs58_otak = bs58::encode(ser_otak).into_string();
subscribe_req.metadata_mut().insert(
X_ONE_TIME_AUTH_TOKEN,
Expand Down
2 changes: 2 additions & 0 deletions apps/jet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub mod transaction_handler;
pub mod transactions;
pub mod util;
pub mod version;
#[cfg(feature = "wincode")]
pub mod wincode_schema;

pub fn setup_tracing(json: bool) -> anyhow::Result<()> {
let env_filter = EnvFilter::builder()
Expand Down
72 changes: 72 additions & 0 deletions apps/jet/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ pub enum PayloadError {
UnsupportedEncoding,
#[error("failed to deserialize transaction: {0}")]
BincodeError(#[from] bincode::Error),
#[cfg(feature = "wincode")]
#[error("failed to serialize/deserialize transaction with wincode: {0}")]
WincodeSerialize(String),
#[error("failed to parse pubkey: {0}")]
InvalidPubkey(#[from] ParsePubkeyError),
#[error("failed to convert proto message: {0}")]
Expand Down Expand Up @@ -128,6 +131,10 @@ impl TransactionPayload {
tx: &VersionedTransaction,
encoding: UiTransactionEncoding,
) -> Result<String, PayloadError> {
#[cfg(feature = "wincode")]
let tx_bytes = crate::wincode_schema::serialize_transaction(tx)
.map_err(|e| PayloadError::WincodeSerialize(e.to_string()))?;
#[cfg(not(feature = "wincode"))]
let tx_bytes = bincode::serialize(tx)?;
Ok(match encoding {
UiTransactionEncoding::Base58 => bs58::encode(tx_bytes).into_string(),
Expand Down Expand Up @@ -264,6 +271,10 @@ impl TryFrom<(&VersionedTransaction, JetRpcSendTransactionConfig)> for Transacti
}
}

#[cfg(feature = "wincode")]
let tx_bytes = crate::wincode_schema::serialize_transaction(transaction)
.map_err(|e| PayloadError::WincodeSerialize(e.to_string()))?;
#[cfg(not(feature = "wincode"))]
let tx_bytes = bincode::serialize(transaction)?;

// Create new payload format with forwarding policies supported
Expand Down Expand Up @@ -302,6 +313,13 @@ impl TransactionDecoder {

let tx_bytes =
TransactionPayload::decode_transaction(&legacy.transaction, encoding)?;

#[cfg(feature = "wincode")]
let tx: VersionedTransaction =
crate::wincode_schema::deserialize_transaction(&tx_bytes)
.map_err(|e| PayloadError::WincodeSerialize(e.to_string()))?;

#[cfg(not(feature = "wincode"))]
let tx = bincode::deserialize(&tx_bytes)?;

// Legacy format doesn't have forwarding policies, so we pass None
Expand All @@ -311,7 +329,14 @@ impl TransactionDecoder {
))
}
TransactionPayload::New(wrapper) => {
#[cfg(feature = "wincode")]
let tx: VersionedTransaction =
crate::wincode_schema::deserialize_transaction(&wrapper.transaction)
.map_err(|e| PayloadError::WincodeSerialize(e.to_string()))?;

#[cfg(not(feature = "wincode"))]
let tx = bincode::deserialize(&wrapper.transaction)?;

let config = if let Some(proto_config) = &wrapper.config {
match proto_config.try_into() {
Ok(config) => Some(config),
Expand Down Expand Up @@ -348,6 +373,8 @@ impl TryFrom<&TransactionConfig> for JetRpcSendTransactionConfig {
mod tests {
use {super::*, crate::util::ms_since_epoch};

// Helper for bincode tests (default, non-wincode)
#[cfg(not(feature = "wincode"))]
fn create_test_wrapper(
tx: &VersionedTransaction,
) -> Result<TransactionWrapper, bincode::Error> {
Expand All @@ -364,6 +391,24 @@ mod tests {
})
}

// Helper for wincode tests
#[cfg(feature = "wincode")]
fn create_test_wrapper(
tx: &VersionedTransaction,
) -> Result<TransactionWrapper, crate::wincode_schema::WincodeError> {
let tx_bytes = crate::wincode_schema::serialize_transaction(tx)?;
Ok(TransactionWrapper {
transaction: tx_bytes,
config: Some(TransactionConfig {
max_retries: Some(5),
forwarding_policies: vec![],
skip_preflight: true,
skip_sanitize: true,
}),
timestamp: Some(1234567890),
})
}

#[test]
fn test_legacy_format() -> Result<(), Box<dyn std::error::Error>> {
let tx = VersionedTransaction::default();
Expand Down Expand Up @@ -395,6 +440,7 @@ mod tests {
}

#[test]
#[cfg(not(feature = "wincode"))]
fn test_new_format_features() -> Result<(), Box<dyn std::error::Error>> {
let tx = VersionedTransaction::default();
let tx_bytes = bincode::serialize(&tx)?;
Expand All @@ -419,6 +465,32 @@ mod tests {
Ok(())
}

#[test]
#[cfg(feature = "wincode")]
fn test_new_format_features() -> Result<(), Box<dyn std::error::Error>> {
let tx = VersionedTransaction::default();
let tx_bytes = crate::wincode_schema::serialize_transaction(&tx)?;

let wrapper = TransactionWrapper {
transaction: tx_bytes,
config: Some(TransactionConfig {
max_retries: Some(5),
forwarding_policies: vec!["test1".to_string()],
skip_preflight: true,
skip_sanitize: true,
}),
timestamp: Some(ms_since_epoch()),
};

let payload = TransactionPayload::New(wrapper);
let (_, config) = TransactionDecoder::decode(&payload)?;

assert!(config.is_some());
let config_with_forwarding_policies = config.unwrap();
assert_eq!(config_with_forwarding_policies.config.max_retries, Some(5));
Ok(())
}

#[test]
fn test_new_format() -> Result<(), Box<dyn std::error::Error>> {
let tx = VersionedTransaction::default();
Expand Down
Loading