Skip to content
Merged
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
96 changes: 95 additions & 1 deletion src/swift_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use crate::{
PROCESS_ORDER_RESPONSE_ERROR_MSG_INVALID_ORDER_AMOUNT,
PROCESS_ORDER_RESPONSE_ERROR_MSG_ORDER_SLOT_TOO_OLD,
PROCESS_ORDER_RESPONSE_ERROR_MSG_VERIFY_SIGNATURE,
PROCESS_ORDER_RESPONSE_IGNORE_PUBKEY, PROCESS_ORDER_RESPONSE_MESSAGE_SUCCESS,
PROCESS_ORDER_RESPONSE_IGNORE_PUBKEY, PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8,
PROCESS_ORDER_RESPONSE_MESSAGE_SUCCESS,
},
types::{unix_now_ms, RequestContext},
},
Expand Down Expand Up @@ -118,6 +119,16 @@ pub async fn process_order_wrapper(
Json(incoming_message): Json<IncomingSignedMessage>,
) -> impl axum::response::IntoResponse {
let context = RequestContext::from_incoming_message(&incoming_message);
if context.is_err() {
return (
axum::http::StatusCode::BAD_REQUEST,
Json(ProcessOrderResponse {
message: PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8,
error: None,
}),
);
}
let context = context.unwrap();

let (status, resp) = match process_order(server_params, incoming_message, false, &context).await
{
Expand Down Expand Up @@ -439,6 +450,16 @@ pub async fn deposit_trade(
.order()
.info(&req.swift_order.taker_authority);
let context = RequestContext::from_incoming_message(&req.swift_order);
if context.is_err() {
return (
axum::http::StatusCode::BAD_REQUEST,
Json(ProcessOrderResponse {
message: PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8,
error: None,
}),
);
}
let context = context.unwrap();
let current_slot = server_params.slot_subscriber.current_slot();

let max_margin_ratio = match extract_signed_message_info(
Expand Down Expand Up @@ -1705,6 +1726,7 @@ mod tests {
accounts::User, SignedMsgOrderParamsDelegateMessage, SignedMsgOrderParamsMessage,
SignedMsgTriggerOrderParams,
};
use ed25519_dalek::Signature as Ed25519Signature;
use solana_sdk::native_token::LAMPORTS_PER_SOL;

fn is_isolated_deposit(signed_msg: &SignedOrderType) -> bool {
Expand Down Expand Up @@ -1882,6 +1904,78 @@ mod tests {
);
}

#[test]
fn test_request_context_from_incoming_message_valid_utf8() {
let taker = Pubkey::new_unique();
let uuid_valid: [u8; 8] = [b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h'];
let authority_msg = SignedOrderType::authority(SignedMsgOrderParamsMessage {
sub_account_id: 0,
signed_msg_order_params: OrderParams {
market_index: 2,
market_type: MarketType::Perp,
order_type: OrderType::Market,
base_asset_amount: LAMPORTS_PER_SOL,
price: 1000,
direction: PositionDirection::Long,
..Default::default()
},
uuid: uuid_valid,
slot: 1000,
stop_loss_order_params: None,
take_profit_order_params: None,
max_margin_ratio: None,
builder_fee_tenth_bps: None,
builder_idx: None,
isolated_position_deposit: None,
});
let msg = IncomingSignedMessage {
taker_pubkey: taker,
signature: Ed25519Signature::from_bytes(&[0u8; 64]).unwrap(),
message: authority_msg,
signing_authority: Pubkey::default(),
taker_authority: Pubkey::default(),
};
let ctx = RequestContext::from_incoming_message(&msg).expect("valid utf8 uuid");
assert_eq!(ctx.order_uuid, "abcdefgh");
assert_eq!(ctx.market_index, 2);
assert_eq!(ctx.market_type, "perp");
assert_eq!(ctx.taker_authority, taker);
}

#[test]
fn test_request_context_from_incoming_message_invalid_utf8() {
let taker = Pubkey::new_unique();
let uuid_invalid: [u8; 8] = [0xFF; 8];
let authority_msg = SignedOrderType::authority(SignedMsgOrderParamsMessage {
sub_account_id: 0,
signed_msg_order_params: OrderParams {
market_index: 0,
market_type: MarketType::Perp,
order_type: OrderType::Market,
base_asset_amount: LAMPORTS_PER_SOL,
price: 1000,
direction: PositionDirection::Long,
..Default::default()
},
uuid: uuid_invalid,
slot: 1000,
stop_loss_order_params: None,
take_profit_order_params: None,
max_margin_ratio: None,
builder_fee_tenth_bps: None,
builder_idx: None,
isolated_position_deposit: None,
});
let msg = IncomingSignedMessage {
taker_pubkey: taker,
signature: Ed25519Signature::from_bytes(&[0u8; 64]).unwrap(),
message: authority_msg,
signing_authority: Pubkey::default(),
taker_authority: Pubkey::default(),
};
assert!(RequestContext::from_incoming_message(&msg).is_err());
}

#[test]
fn test_extract_signed_message_info_delegated() {
let taker_authority = Pubkey::new_unique();
Expand Down
3 changes: 2 additions & 1 deletion src/types/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ pub const PROCESS_ORDER_RESPONSE_ERROR_MSG_INVALID_ORDER_AMOUNT: &str =
"Invalid base_asset_amount in tp/sl";
pub const PROCESS_ORDER_RESPONSE_ERROR_MSG_DELIVERY_FAILED: &str = "Failed to deliver message";
pub const PROCESS_ORDER_RESPONSE_IGNORE_PUBKEY: &str = "Ignore pubkey";
pub const PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8: &str = "Order uuid invalid utf8";

#[derive(serde::Deserialize, Clone, Debug)]
#[serde(rename_all = "lowercase")]
Expand Down Expand Up @@ -449,7 +450,7 @@ mod tests {

if let SignedOrderType::Authority {
inner: signed_msg,
raw,
raw: _,
} = actual.order()
{
let expected = SignedMsgOrderParamsMessage {
Expand Down
72 changes: 54 additions & 18 deletions src/types/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,32 +49,68 @@ pub struct RequestContext {
}

impl RequestContext {
pub fn from_incoming_message(msg: &IncomingSignedMessage) -> Self {
pub fn from_incoming_message(msg: &IncomingSignedMessage) -> Result<Self, ()> {
let recv_ts = unix_now_ms();
let info = msg.order().info(&msg.taker_authority);
let market_index = info.order_params.market_index;
let market_type = match info.order_params.market_type {
MarketType::Perp => "perp",
MarketType::Spot => "spot",
};
let order_uuid = core::str::from_utf8(&info.uuid)
.unwrap_or("no uuid ????????")
.to_string();
let taker_authority = if msg.taker_authority != Pubkey::default() {
msg.taker_authority
} else {
msg.taker_pubkey
};
match core::str::from_utf8(&info.uuid) {
Ok(order_uuid) => {
let taker_authority = if msg.taker_authority != Pubkey::default() {
msg.taker_authority
} else {
msg.taker_pubkey
};

Self {
market_index,
market_type,
recv_ts,
log_prefix: format!(
"[order uuid={order_uuid} market={market_type}:{market_index} taker={taker_authority}]"
),
taker_authority,
order_uuid,
Ok(Self {
market_index,
market_type,
recv_ts,
log_prefix: format!(
"[order uuid={order_uuid} market={market_type}:{market_index} taker={taker_authority}]"
),
taker_authority,
order_uuid: order_uuid.to_string(),
})
}
Err(_) => {
let taker_authority = if msg.taker_authority != Pubkey::default() {
msg.taker_authority
} else {
msg.taker_pubkey
};
log::info!(
target: "swift",
"[order market={market_type}:{market_index} taker={taker_authority}] uuid invalid utf-8"
);
Err(())
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn from_incoming_message_valid_utf8_uuid() {
// Payload from messages::tests with uuid [115, 56, 108, 117, 74, 76, 90, 101] = "s8luJLZe"
let json = r#"{
"market_index": 2,
"market_type": "perp",
"message": "c8d5a65e2234f55d0001010080841e00000000000000000000000000020000000000000000013201bb60507d000000000117c0127c000000000000272108160000000073386c754a4c5a650000",
"signature": "H8HRloc2vBdhHyiNK5W/Shv3kVKmIYsHTBzlD2ecyxyOUh7EuysU/Y5AOXZ3IpsMxRyLn6OSAHKEgCIQX4OpDQ==",
"signing_authority": "4rmhwytmKH1XsgGAUyUUH7U64HS5FtT6gM8HGKAfwcFE",
"taker_pubkey": "4rmhwytmKH1XsgGAUyUUH7U64HS5FtT6gM8HGKAfwcFE"
}"#;
let msg: IncomingSignedMessage = serde_json::from_str(json).expect("deserialize");
let ctx = RequestContext::from_incoming_message(&msg).expect("valid utf8 uuid");
assert_eq!(ctx.order_uuid, "s8luJLZe");
assert_eq!(ctx.market_index, 2);
assert_eq!(ctx.market_type, "perp");
}
}
Loading