From ca4e11168d86ef537d48a0ce38a6fd9d83a9babd Mon Sep 17 00:00:00 2001 From: Dzejkop Date: Tue, 4 Jun 2024 14:50:16 +0200 Subject: [PATCH] Better wrappers + send tx --- src/new_server.rs | 87 +++++++++++++++--------------- src/types.rs | 20 ++++--- src/types/wrappers.rs | 1 + src/types/wrappers/decimal_u256.rs | 10 ++-- src/types/wrappers/hex_bytes.rs | 86 +++++++++++++++++++++++++++++ src/types/wrappers/hex_u256.rs | 9 ++-- 6 files changed, 156 insertions(+), 57 deletions(-) create mode 100644 src/types/wrappers/hex_bytes.rs diff --git a/src/new_server.rs b/src/new_server.rs index 698bbd1..776a48a 100644 --- a/src/new_server.rs +++ b/src/new_server.rs @@ -19,7 +19,7 @@ use crate::service::Service; use crate::task_runner::TaskRunner; use crate::types::{ CreateRelayerRequest, CreateRelayerResponse, NetworkInfo, NewNetworkInfo, - RelayerInfo, RelayerUpdate, SendTxResponse, + RelayerInfo, RelayerUpdate, SendTxRequest, SendTxResponse, }; mod security; @@ -262,52 +262,53 @@ impl RelayerApi { &self, Data(app): Data<&Arc>, Path(api_token): Path, - // Json(req): Json, + Json(req): Json, ) -> Result> { api_token.validate(app).await?; - // let tx_id = if let Some(id) = req.tx_id { - // id - // } else { - // uuid::Uuid::new_v4().to_string() - // }; - - // let relayer = app.db.get_relayer(api_token.relayer_id()).await?; - - // if !relayer.enabled { - // return Err(ApiError::RelayerDisabled); - // } - - // let relayer_queued_tx_count = app - // .db - // .get_relayer_pending_txs(api_token.relayer_id()) - // .await?; - - // if relayer_queued_tx_count > relayer.max_queued_txs as usize { - // return Err(ApiError::TooManyTransactions { - // max: relayer.max_queued_txs as usize, - // current: relayer_queued_tx_count, - // }); - // } - - // app.db - // .create_transaction( - // &tx_id, - // req.to, - // req.data.as_ref().map(|d| &d[..]).unwrap_or(&[]), - // req.value, - // req.gas_limit, - // req.priority, - // req.blobs, - // api_token.relayer_id(), - // ) - // .await?; - - // tracing::info!(tx_id, "Transaction created"); - - // Ok(Json(SendTxResponse { tx_id })) + let tx_id = if let Some(id) = req.tx_id { + id + } else { + uuid::Uuid::new_v4().to_string() + }; - todo!() + let relayer = app.db.get_relayer(api_token.relayer_id()).await?; + + if !relayer.enabled { + return Err(poem::error::Error::from_string( + "Relayer is disabled".to_string(), + StatusCode::FORBIDDEN, + )); + } + + let relayer_queued_tx_count = app + .db + .get_relayer_pending_txs(api_token.relayer_id()) + .await?; + + if relayer_queued_tx_count > relayer.max_queued_txs as usize { + return Err(poem::error::Error::from_string( + "Relayer queue is full".to_string(), + StatusCode::TOO_MANY_REQUESTS, + )); + } + + app.db + .create_transaction( + &tx_id, + req.to.0, + req.data.as_ref().map(|d| &d.0[..]).unwrap_or(&[]), + req.value.0, + req.gas_limit.0, + req.priority, + req.blobs, + api_token.relayer_id(), + ) + .await?; + + tracing::info!(tx_id, "Transaction created"); + + Ok(Json(SendTxResponse { tx_id })) } /// Get Transaction diff --git a/src/types.rs b/src/types.rs index 64cdbfd..d424c52 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,13 +1,15 @@ -use poem_openapi::Object; +use poem_openapi::{Enum, Object}; use serde::{Deserialize, Serialize}; use sqlx::prelude::FromRow; use wrappers::address::AddressWrapper; +use wrappers::hex_bytes::HexBytes; use wrappers::hex_u256::HexU256; pub mod wrappers; -#[derive(Deserialize, Serialize, Debug, Clone, Copy, Default, sqlx::Type)] +#[derive(Deserialize, Serialize, Debug, Clone, Copy, Default, sqlx::Type, Enum)] #[serde(rename_all = "camelCase")] +#[oai(rename_all = "camelCase")] #[sqlx(type_name = "transaction_priority", rename_all = "camelCase")] pub enum TransactionPriority { // 5th percentile @@ -113,7 +115,6 @@ pub struct CreateRelayerRequest { pub struct CreateRelayerResponse { /// ID of the created relayer pub relayer_id: String, - // TODO: Make type safe /// Address of the created relayer pub address: AddressWrapper, } @@ -124,14 +125,19 @@ pub struct CreateRelayerResponse { pub struct SendTxRequest { pub to: AddressWrapper, pub value: HexU256, - // #[serde(default)] - // pub data: Option, + #[serde(default)] + #[oai(default)] + pub data: Option, pub gas_limit: HexU256, - // #[serde(default)] - // pub priority: TransactionPriority, #[serde(default)] + #[oai(default)] + pub priority: TransactionPriority, + #[serde(default)] + #[oai(default)] pub tx_id: Option, + // TODO: poem_openapi thinks this is a nested array of numbers #[serde(default, with = "crate::serde_utils::base64_binary")] + #[oai(default)] pub blobs: Option>>, } diff --git a/src/types/wrappers.rs b/src/types/wrappers.rs index fd52d0c..acff1d0 100644 --- a/src/types/wrappers.rs +++ b/src/types/wrappers.rs @@ -3,6 +3,7 @@ use hex_u256::HexU256; pub mod address; pub mod h256; +pub mod hex_bytes; // TODO: Remove repeated code in these 2 modules pub mod decimal_u256; diff --git a/src/types/wrappers/decimal_u256.rs b/src/types/wrappers/decimal_u256.rs index b47872b..a3d1922 100644 --- a/src/types/wrappers/decimal_u256.rs +++ b/src/types/wrappers/decimal_u256.rs @@ -94,17 +94,19 @@ impl poem_openapi::types::Type for DecimalU256 { } fn schema_ref() -> MetaSchemaRef { - let mut schema_ref = MetaSchema::new_with_format("string", "u256"); + let mut schema_ref = MetaSchema::new_with_format("u256", "decimal"); schema_ref.example = - Some(serde_json::Value::String("0xff".to_string())); - schema_ref.title = Some("Address".to_string()); + Some(serde_json::Value::String("0".to_string())); + schema_ref.default = + Some(serde_json::Value::String("0".to_string())); + schema_ref.title = Some("Decimal U256".to_string()); schema_ref.description = Some( "A 256-bit unsigned integer. Supports hex and decimal encoding", ); MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format( - "string", "u256", + "u256", "decimal", ))) } diff --git a/src/types/wrappers/hex_bytes.rs b/src/types/wrappers/hex_bytes.rs new file mode 100644 index 0000000..66a7813 --- /dev/null +++ b/src/types/wrappers/hex_bytes.rs @@ -0,0 +1,86 @@ +use ethers::types::Bytes; +use serde::{Deserialize, Serialize}; +use poem_openapi::registry::{MetaSchema, MetaSchemaRef}; +use poem_openapi::types::{ParseFromJSON, ToJSON}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +pub struct HexBytes(pub Bytes); + +impl From for HexBytes { + fn from(value: Bytes) -> Self { + Self(value) + } +} + +impl poem_openapi::types::Type for HexBytes { + const IS_REQUIRED: bool = true; + + type RawValueType = Self; + + type RawElementValueType = Self; + + fn name() -> std::borrow::Cow<'static, str> { + "string(bytes)".into() + } + + fn schema_ref() -> MetaSchemaRef { + let mut schema_ref = MetaSchema::new_with_format("string", "bytes"); + + schema_ref.example = Some(serde_json::Value::String( + "0xffffff".to_string(), + )); + schema_ref.title = Some("Bytes".to_string()); + schema_ref.description = Some("Hex encoded binary blob"); + + MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format( + "string", "bytes", + ))) + } + + fn as_raw_value(&self) -> Option<&Self::RawValueType> { + Some(self) + } + + fn raw_element_iter<'a>( + &'a self, + ) -> Box + 'a> { + Box::new(self.as_raw_value().into_iter()) + } +} + +impl ParseFromJSON for HexBytes { + fn parse_from_json( + value: Option, + ) -> poem_openapi::types::ParseResult { + // TODO: Better error handling + let value = value + .ok_or_else(|| poem_openapi::types::ParseError::expected_input())?; + + let inner = serde_json::from_value(value) + .map_err(|_| poem_openapi::types::ParseError::expected_input())?; + + Ok(Self(inner)) + } +} + +impl ToJSON for HexBytes { + fn to_json(&self) -> Option { + serde_json::to_value(&self.0).ok() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use test_case::test_case; + + #[test_case("0xff", vec![255])] + #[test_case("0xffff", vec![255, 255])] + #[test_case("0x0101", vec![1, 1])] + fn deserialize_string(s: &str, v: Vec) { + let value = serde_json::Value::String(s.to_string()); + let result = HexBytes::parse_from_json(Some(value)).unwrap(); + assert_eq!(result.0, Bytes::from(v)); + } +} diff --git a/src/types/wrappers/hex_u256.rs b/src/types/wrappers/hex_u256.rs index 5c27b93..3e9b742 100644 --- a/src/types/wrappers/hex_u256.rs +++ b/src/types/wrappers/hex_u256.rs @@ -72,16 +72,19 @@ impl poem_openapi::types::Type for HexU256 { } fn schema_ref() -> MetaSchemaRef { - let mut schema_ref = MetaSchema::new_with_format("string", "u256"); + let mut schema_ref = MetaSchema::new_with_format("u256", "hex"); schema_ref.example = Some(serde_json::Value::String( "0xff".to_string(), )); - schema_ref.title = Some("Address".to_string()); + schema_ref.default = Some(serde_json::Value::String( + "0x0".to_string(), + )); + schema_ref.title = Some("Hex U256".to_string()); schema_ref.description = Some("A 256-bit unsigned integer. Supports hex and decimal encoding"); MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format( - "string", "u256", + "u256", "hex", ))) }