Skip to content

Commit 00fda7c

Browse files
authored
Merge pull request #110 from drift-labs/fix/invalid-utf8
Fix/invalid utf8
2 parents cf5f509 + 47809bd commit 00fda7c

File tree

3 files changed

+151
-20
lines changed

3 files changed

+151
-20
lines changed

src/swift_server.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use crate::{
1818
PROCESS_ORDER_RESPONSE_ERROR_MSG_INVALID_ORDER_AMOUNT,
1919
PROCESS_ORDER_RESPONSE_ERROR_MSG_ORDER_SLOT_TOO_OLD,
2020
PROCESS_ORDER_RESPONSE_ERROR_MSG_VERIFY_SIGNATURE,
21-
PROCESS_ORDER_RESPONSE_IGNORE_PUBKEY, PROCESS_ORDER_RESPONSE_MESSAGE_SUCCESS,
21+
PROCESS_ORDER_RESPONSE_IGNORE_PUBKEY, PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8,
22+
PROCESS_ORDER_RESPONSE_MESSAGE_SUCCESS,
2223
},
2324
types::{unix_now_ms, RequestContext},
2425
},
@@ -118,6 +119,16 @@ pub async fn process_order_wrapper(
118119
Json(incoming_message): Json<IncomingSignedMessage>,
119120
) -> impl axum::response::IntoResponse {
120121
let context = RequestContext::from_incoming_message(&incoming_message);
122+
if context.is_err() {
123+
return (
124+
axum::http::StatusCode::BAD_REQUEST,
125+
Json(ProcessOrderResponse {
126+
message: PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8,
127+
error: None,
128+
}),
129+
);
130+
}
131+
let context = context.unwrap();
121132

122133
let (status, resp) = match process_order(server_params, incoming_message, false, &context).await
123134
{
@@ -439,6 +450,16 @@ pub async fn deposit_trade(
439450
.order()
440451
.info(&req.swift_order.taker_authority);
441452
let context = RequestContext::from_incoming_message(&req.swift_order);
453+
if context.is_err() {
454+
return (
455+
axum::http::StatusCode::BAD_REQUEST,
456+
Json(ProcessOrderResponse {
457+
message: PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8,
458+
error: None,
459+
}),
460+
);
461+
}
462+
let context = context.unwrap();
442463
let current_slot = server_params.slot_subscriber.current_slot();
443464

444465
let max_margin_ratio = match extract_signed_message_info(
@@ -1705,6 +1726,7 @@ mod tests {
17051726
accounts::User, SignedMsgOrderParamsDelegateMessage, SignedMsgOrderParamsMessage,
17061727
SignedMsgTriggerOrderParams,
17071728
};
1729+
use ed25519_dalek::Signature as Ed25519Signature;
17081730
use solana_sdk::native_token::LAMPORTS_PER_SOL;
17091731

17101732
fn is_isolated_deposit(signed_msg: &SignedOrderType) -> bool {
@@ -1882,6 +1904,78 @@ mod tests {
18821904
);
18831905
}
18841906

1907+
#[test]
1908+
fn test_request_context_from_incoming_message_valid_utf8() {
1909+
let taker = Pubkey::new_unique();
1910+
let uuid_valid: [u8; 8] = [b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h'];
1911+
let authority_msg = SignedOrderType::authority(SignedMsgOrderParamsMessage {
1912+
sub_account_id: 0,
1913+
signed_msg_order_params: OrderParams {
1914+
market_index: 2,
1915+
market_type: MarketType::Perp,
1916+
order_type: OrderType::Market,
1917+
base_asset_amount: LAMPORTS_PER_SOL,
1918+
price: 1000,
1919+
direction: PositionDirection::Long,
1920+
..Default::default()
1921+
},
1922+
uuid: uuid_valid,
1923+
slot: 1000,
1924+
stop_loss_order_params: None,
1925+
take_profit_order_params: None,
1926+
max_margin_ratio: None,
1927+
builder_fee_tenth_bps: None,
1928+
builder_idx: None,
1929+
isolated_position_deposit: None,
1930+
});
1931+
let msg = IncomingSignedMessage {
1932+
taker_pubkey: taker,
1933+
signature: Ed25519Signature::from_bytes(&[0u8; 64]).unwrap(),
1934+
message: authority_msg,
1935+
signing_authority: Pubkey::default(),
1936+
taker_authority: Pubkey::default(),
1937+
};
1938+
let ctx = RequestContext::from_incoming_message(&msg).expect("valid utf8 uuid");
1939+
assert_eq!(ctx.order_uuid, "abcdefgh");
1940+
assert_eq!(ctx.market_index, 2);
1941+
assert_eq!(ctx.market_type, "perp");
1942+
assert_eq!(ctx.taker_authority, taker);
1943+
}
1944+
1945+
#[test]
1946+
fn test_request_context_from_incoming_message_invalid_utf8() {
1947+
let taker = Pubkey::new_unique();
1948+
let uuid_invalid: [u8; 8] = [0xFF; 8];
1949+
let authority_msg = SignedOrderType::authority(SignedMsgOrderParamsMessage {
1950+
sub_account_id: 0,
1951+
signed_msg_order_params: OrderParams {
1952+
market_index: 0,
1953+
market_type: MarketType::Perp,
1954+
order_type: OrderType::Market,
1955+
base_asset_amount: LAMPORTS_PER_SOL,
1956+
price: 1000,
1957+
direction: PositionDirection::Long,
1958+
..Default::default()
1959+
},
1960+
uuid: uuid_invalid,
1961+
slot: 1000,
1962+
stop_loss_order_params: None,
1963+
take_profit_order_params: None,
1964+
max_margin_ratio: None,
1965+
builder_fee_tenth_bps: None,
1966+
builder_idx: None,
1967+
isolated_position_deposit: None,
1968+
});
1969+
let msg = IncomingSignedMessage {
1970+
taker_pubkey: taker,
1971+
signature: Ed25519Signature::from_bytes(&[0u8; 64]).unwrap(),
1972+
message: authority_msg,
1973+
signing_authority: Pubkey::default(),
1974+
taker_authority: Pubkey::default(),
1975+
};
1976+
assert!(RequestContext::from_incoming_message(&msg).is_err());
1977+
}
1978+
18851979
#[test]
18861980
fn test_extract_signed_message_info_delegated() {
18871981
let taker_authority = Pubkey::new_unique();

src/types/messages.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ pub const PROCESS_ORDER_RESPONSE_ERROR_MSG_INVALID_ORDER_AMOUNT: &str =
154154
"Invalid base_asset_amount in tp/sl";
155155
pub const PROCESS_ORDER_RESPONSE_ERROR_MSG_DELIVERY_FAILED: &str = "Failed to deliver message";
156156
pub const PROCESS_ORDER_RESPONSE_IGNORE_PUBKEY: &str = "Ignore pubkey";
157+
pub const PROCESS_ORDER_RESPONSE_INVALID_UUID_UTF8: &str = "Order uuid invalid utf8";
157158

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

450451
if let SignedOrderType::Authority {
451452
inner: signed_msg,
452-
raw,
453+
raw: _,
453454
} = actual.order()
454455
{
455456
let expected = SignedMsgOrderParamsMessage {

src/types/types.rs

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,32 +49,68 @@ pub struct RequestContext {
4949
}
5050

5151
impl RequestContext {
52-
pub fn from_incoming_message(msg: &IncomingSignedMessage) -> Self {
52+
pub fn from_incoming_message(msg: &IncomingSignedMessage) -> Result<Self, ()> {
5353
let recv_ts = unix_now_ms();
5454
let info = msg.order().info(&msg.taker_authority);
5555
let market_index = info.order_params.market_index;
5656
let market_type = match info.order_params.market_type {
5757
MarketType::Perp => "perp",
5858
MarketType::Spot => "spot",
5959
};
60-
let order_uuid = core::str::from_utf8(&info.uuid)
61-
.unwrap_or("no uuid ????????")
62-
.to_string();
63-
let taker_authority = if msg.taker_authority != Pubkey::default() {
64-
msg.taker_authority
65-
} else {
66-
msg.taker_pubkey
67-
};
60+
match core::str::from_utf8(&info.uuid) {
61+
Ok(order_uuid) => {
62+
let taker_authority = if msg.taker_authority != Pubkey::default() {
63+
msg.taker_authority
64+
} else {
65+
msg.taker_pubkey
66+
};
6867

69-
Self {
70-
market_index,
71-
market_type,
72-
recv_ts,
73-
log_prefix: format!(
74-
"[order uuid={order_uuid} market={market_type}:{market_index} taker={taker_authority}]"
75-
),
76-
taker_authority,
77-
order_uuid,
68+
Ok(Self {
69+
market_index,
70+
market_type,
71+
recv_ts,
72+
log_prefix: format!(
73+
"[order uuid={order_uuid} market={market_type}:{market_index} taker={taker_authority}]"
74+
),
75+
taker_authority,
76+
order_uuid: order_uuid.to_string(),
77+
})
78+
}
79+
Err(_) => {
80+
let taker_authority = if msg.taker_authority != Pubkey::default() {
81+
msg.taker_authority
82+
} else {
83+
msg.taker_pubkey
84+
};
85+
log::info!(
86+
target: "swift",
87+
"[order market={market_type}:{market_index} taker={taker_authority}] uuid invalid utf-8"
88+
);
89+
Err(())
90+
}
7891
}
7992
}
8093
}
94+
95+
#[cfg(test)]
96+
mod tests {
97+
use super::*;
98+
99+
#[test]
100+
fn from_incoming_message_valid_utf8_uuid() {
101+
// Payload from messages::tests with uuid [115, 56, 108, 117, 74, 76, 90, 101] = "s8luJLZe"
102+
let json = r#"{
103+
"market_index": 2,
104+
"market_type": "perp",
105+
"message": "c8d5a65e2234f55d0001010080841e00000000000000000000000000020000000000000000013201bb60507d000000000117c0127c000000000000272108160000000073386c754a4c5a650000",
106+
"signature": "H8HRloc2vBdhHyiNK5W/Shv3kVKmIYsHTBzlD2ecyxyOUh7EuysU/Y5AOXZ3IpsMxRyLn6OSAHKEgCIQX4OpDQ==",
107+
"signing_authority": "4rmhwytmKH1XsgGAUyUUH7U64HS5FtT6gM8HGKAfwcFE",
108+
"taker_pubkey": "4rmhwytmKH1XsgGAUyUUH7U64HS5FtT6gM8HGKAfwcFE"
109+
}"#;
110+
let msg: IncomingSignedMessage = serde_json::from_str(json).expect("deserialize");
111+
let ctx = RequestContext::from_incoming_message(&msg).expect("valid utf8 uuid");
112+
assert_eq!(ctx.order_uuid, "s8luJLZe");
113+
assert_eq!(ctx.market_index, 2);
114+
assert_eq!(ctx.market_type, "perp");
115+
}
116+
}

0 commit comments

Comments
 (0)