Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 9449b03

Browse files
salman01zpshekohex
andauthored
Substrate tx-queue tracking and http endpoints for processing withdraw tx (#551)
Co-authored-by: shekohex <dev+github@shadykhalifa.me>
1 parent 1611ee5 commit 9449b03

File tree

21 files changed

+340
-650
lines changed

21 files changed

+340
-650
lines changed

crates/relayer-handler-utils/src/lib.rs

Lines changed: 4 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
#![allow(missing_docs)]
1717

1818
use serde::{Deserialize, Deserializer, Serialize};
19-
use tokio::sync::mpsc;
2019
use webb::evm::ethers::abi::Address;
21-
use webb::evm::ethers::prelude::{ContractError, I256, U128};
22-
use webb::evm::ethers::providers::Middleware;
20+
use webb::evm::ethers::prelude::{I256, U128};
21+
2322
use webb::evm::ethers::types::Bytes;
24-
use webb::evm::ethers::types::{H256, H512, U256};
25-
use webb_relayer_store::queue::QueueItemState;
23+
use webb::evm::ethers::types::{H256, U256};
24+
2625
use webb_relayer_tx_relay_utils::VAnchorRelayTransaction;
2726

2827
/// Representation for IP address response
@@ -69,111 +68,6 @@ impl<'de> Deserialize<'de> for WebbI128 {
6968
}
7069
}
7170

72-
/// Type of Command to use
73-
#[derive(Debug, Clone, Deserialize)]
74-
#[serde(rename_all = "camelCase")]
75-
pub enum Command {
76-
/// Substrate specific subcommand.
77-
Substrate(SubstrateCommandType),
78-
/// EVM specific subcommand.
79-
Evm(EvmCommandType),
80-
/// Ping?
81-
Ping(),
82-
}
83-
84-
/// Enumerates the supported evm commands for relaying transactions
85-
#[derive(Debug, Clone, Deserialize)]
86-
#[serde(rename_all = "camelCase")]
87-
pub enum EvmCommandType {
88-
/// Webb Variable Anchors.
89-
VAnchor(EvmVanchorCommand),
90-
}
91-
92-
/// Enumerates the supported substrate commands for relaying transactions
93-
#[derive(Debug, Clone, Deserialize)]
94-
#[serde(rename_all = "camelCase")]
95-
pub enum SubstrateCommandType {
96-
/// Webb Variable Anchors.
97-
VAnchor(SubstrateVAchorCommand),
98-
}
99-
100-
/// Enumerates the command responses
101-
#[derive(Debug, Clone, PartialEq, Serialize)]
102-
#[serde(rename_all = "camelCase")]
103-
pub enum CommandResponse {
104-
/// Pong?
105-
Pong(),
106-
/// Network Status
107-
Network(NetworkStatus),
108-
/// Withdrawal Status
109-
Withdraw(WithdrawStatus),
110-
/// An error occurred
111-
Error(String),
112-
/// Tx status
113-
TxStatus {
114-
/// The transaction item key.
115-
#[serde(rename = "itemKey")]
116-
item_key: H512,
117-
status: QueueItemState,
118-
},
119-
}
120-
/// Enumerates the network status response of the relayer
121-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
122-
#[serde(rename_all = "camelCase")]
123-
pub enum NetworkStatus {
124-
/// Relayer is connecting to the network.
125-
Connecting,
126-
/// Relayer is connected to the network.
127-
Connected,
128-
/// Network failure with error message.
129-
Failed {
130-
/// Error message
131-
reason: String,
132-
},
133-
/// Relayer is disconnected from the network.
134-
Disconnected,
135-
/// This contract is not supported by the relayer.
136-
UnsupportedContract,
137-
/// This network (chain) is not supported by the relayer.
138-
UnsupportedChain,
139-
/// Invalid Relayer address in the proof
140-
InvalidRelayerAddress,
141-
}
142-
/// Enumerates the withdraw status response of the relayer
143-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
144-
#[serde(rename_all = "camelCase")]
145-
pub enum WithdrawStatus {
146-
/// The transaction is sent to the network.
147-
Sent,
148-
/// The transaction is submitted to the network.
149-
Submitted {
150-
/// The transaction hash.
151-
#[serde(rename = "txHash")]
152-
tx_hash: H256,
153-
},
154-
/// The transaction is in the block.
155-
Finalized {
156-
/// The transaction hash.
157-
#[serde(rename = "txHash")]
158-
tx_hash: H256,
159-
},
160-
/// Valid transaction.
161-
Valid,
162-
/// Invalid Merkle roots.
163-
InvalidMerkleRoots,
164-
/// Transaction dropped from mempool, send it again.
165-
DroppedFromMemPool,
166-
/// Invalid transaction.
167-
Errored {
168-
/// Error Code.
169-
code: i32,
170-
/// Error Message.
171-
reason: String,
172-
},
173-
}
174-
175-
/// Type alias for mpsc::Sender<CommandResponse>
176-
pub type CommandStream = mpsc::Sender<CommandResponse>;
17771
/// The command type for EVM vanchor transactions
17872
pub type EvmVanchorCommand = VAnchorRelayTransaction<
17973
Address, // Contract address
@@ -198,38 +92,3 @@ type T = u32; // Substrate assetId
19892
/// The command type for Substrate vanchor txes
19993
pub type SubstrateVAchorCommand =
20094
VAnchorRelayTransaction<Id, P, R, E, I, B, A, T>;
201-
202-
/// A helper function to extract the error code and the reason from EVM errors.
203-
pub fn into_withdraw_error<M: Middleware>(
204-
e: ContractError<M>,
205-
) -> WithdrawStatus {
206-
// a poor man error parser
207-
// WARNING: **don't try this at home**.
208-
let msg = format!("{e}");
209-
// split the error into words, lazily.
210-
let mut words = msg.split_whitespace();
211-
let mut reason = "unknown".to_string();
212-
let mut code = -1;
213-
214-
while let Some(current_word) = words.next() {
215-
if current_word == "(code:" {
216-
code = match words.next() {
217-
Some(val) => {
218-
let mut v = val.to_string();
219-
v.pop(); // remove ","
220-
v.parse().unwrap_or(-1)
221-
}
222-
_ => -1, // unknown code
223-
};
224-
} else if current_word == "message:" {
225-
// next we need to collect all words in between "message:"
226-
// and "data:", that would be the error message.
227-
let msg: Vec<_> =
228-
words.clone().take_while(|v| *v != "data:").collect();
229-
reason = msg.join(" ");
230-
reason.pop(); // remove the "," at the end.
231-
}
232-
}
233-
234-
WithdrawStatus::Errored { reason, code }
235-
}

crates/relayer-handlers/src/lib.rs

Lines changed: 1 addition & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -12,219 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
//! Relayer handlers for HTTP/Socket calls
16-
17-
#![allow(clippy::large_enum_variant)]
18-
#![warn(missing_docs)]
19-
use axum::extract::{Path, State, WebSocketUpgrade};
20-
use ethereum_types::{Address, U256};
21-
use std::error::Error;
22-
use std::sync::Arc;
23-
24-
use futures::prelude::*;
25-
26-
use axum::extract::ws::{Message, WebSocket};
27-
use axum::http::StatusCode;
28-
use axum::response::Response;
29-
use axum::Json;
30-
use axum_client_ip::InsecureClientIp;
31-
use tokio::sync::mpsc;
32-
use tokio_stream::wrappers::ReceiverStream;
33-
use webb_proposals::TypedChainId;
34-
35-
use webb_relayer_context::RelayerContext;
36-
use webb_relayer_handler_utils::{
37-
Command, CommandResponse, CommandStream, IpInformationResponse,
38-
SubstrateCommandType,
39-
};
40-
use webb_relayer_tx_relay::evm::fees::{get_evm_fee_info, EvmFeeInfo};
41-
42-
use webb_relayer_tx_relay::substrate::fees::{
43-
get_substrate_fee_info, SubstrateFeeInfo,
44-
};
45-
use webb_relayer_tx_relay::substrate::vanchor::handle_substrate_vanchor_relay_tx;
46-
use webb_relayer_utils::HandlerError;
15+
//! Relayer handlers for HTTP calls.
4716
4817
/// Module handles relayer API
4918
pub mod routes;
50-
51-
/// Wait for websocket connection upgrade
52-
pub async fn websocket_handler(
53-
ws: WebSocketUpgrade,
54-
State(ctx): State<Arc<RelayerContext>>,
55-
) -> Response {
56-
ws.on_upgrade(move |socket| accept_websocket_connection(socket, ctx))
57-
}
58-
59-
/// Sets up a websocket connection.
60-
///
61-
/// # Arguments
62-
///
63-
/// * `ctx` - RelayContext reference that holds the configuration
64-
/// * `stream` - Websocket stream
65-
async fn accept_websocket_connection(ws: WebSocket, ctx: Arc<RelayerContext>) {
66-
let (mut tx, mut rx) = ws.split();
67-
68-
// Wait for client to send over text (such as relay transaction requests)
69-
while let Some(msg) = rx.next().await {
70-
match msg {
71-
Ok(msg) => {
72-
if let Ok(text) = msg.to_text() {
73-
// Use inspect_err() here once stabilized
74-
let _ =
75-
handle_text(&ctx, text, &mut tx).await.map_err(|e| {
76-
tracing::warn!("Websocket handler error: {e}")
77-
});
78-
}
79-
}
80-
Err(e) => {
81-
tracing::warn!("Websocket error: {e}");
82-
return;
83-
}
84-
}
85-
}
86-
}
87-
88-
/// Sets up a websocket channels for message sending.
89-
///
90-
/// This is primarily used for transaction relaying. The intention is
91-
/// that a user will send formatted relay requests to the relayer using
92-
/// the websocket. The command will be extracted and sent to `handle_cmd`
93-
/// if successfully deserialized.
94-
///
95-
/// Returns `Ok(())` on success
96-
///
97-
/// # Arguments
98-
///
99-
/// * `ctx` - RelayContext reference that holds the configuration
100-
/// * `v` - The text (usually in a JSON form) message to be handled.
101-
/// * `tx` - A mutable Trait implementation of the `warp::ws::Sender` trait
102-
pub async fn handle_text<TX>(
103-
ctx: &RelayerContext,
104-
v: &str,
105-
tx: &mut TX,
106-
) -> webb_relayer_utils::Result<()>
107-
where
108-
TX: Sink<Message> + Unpin,
109-
TX::Error: Error + Send + Sync + 'static,
110-
{
111-
// for every connection, we create a new channel, where we will use to send messages
112-
// over it.
113-
let (my_tx, my_rx) = mpsc::channel(50);
114-
let res_stream = ReceiverStream::new(my_rx);
115-
match serde_json::from_str(v) {
116-
Ok(cmd) => {
117-
if let Err(e) = handle_cmd(ctx.clone(), cmd, my_tx.clone()).await {
118-
tracing::error!("{:?}", e);
119-
let _ = my_tx.send(e).await;
120-
}
121-
// Send back the response, usually a transaction hash
122-
// from processing the transaction relaying command.
123-
res_stream
124-
.fuse()
125-
.map(|v| serde_json::to_string(&v).expect("bad value"))
126-
.inspect(|v| tracing::trace!("Sending: {}", v))
127-
.map(Message::Text)
128-
.map(Result::Ok)
129-
.forward(tx)
130-
.map_err(|_| webb_relayer_utils::Error::FailedToSendResponse)
131-
.await?;
132-
}
133-
Err(e) => {
134-
tracing::warn!("Got invalid payload: {:?}", e);
135-
tracing::debug!("Invalid payload: {:?}", v);
136-
let error = CommandResponse::Error(e.to_string());
137-
let value = serde_json::to_string(&error)?;
138-
tx.send(Message::Text(value))
139-
.map_err(|_| webb_relayer_utils::Error::FailedToSendResponse)
140-
.await?;
141-
}
142-
};
143-
Ok(())
144-
}
145-
146-
/// Handles the socket address response
147-
///
148-
/// Returns a Result with the `IpInformationResponse` on success
149-
///
150-
/// # Arguments
151-
///
152-
/// * `ip` - Extractor for client IP, taking into account x-forwarded-for and similar headers
153-
pub async fn handle_socket_info(
154-
InsecureClientIp(ip): InsecureClientIp,
155-
) -> Json<IpInformationResponse> {
156-
Json(IpInformationResponse { ip: ip.to_string() })
157-
}
158-
159-
/// Handles the command prompts for EVM and Substrate chains
160-
///
161-
/// # Arguments
162-
///
163-
/// * `ctx` - RelayContext reference that holds the configuration
164-
/// * `cmd` - The command to execute
165-
/// * `stream` - The stream to write the response to
166-
pub async fn handle_cmd(
167-
ctx: RelayerContext,
168-
cmd: Command,
169-
stream: CommandStream,
170-
) -> Result<(), CommandResponse> {
171-
if !ctx.config.features.private_tx_relay {
172-
return Err(CommandResponse::Error(
173-
"Private transaction relaying is not enabled.".to_string(),
174-
));
175-
}
176-
177-
match cmd {
178-
Command::Substrate(substrate) => match substrate {
179-
SubstrateCommandType::VAnchor(vanchor) => {
180-
handle_substrate_vanchor_relay_tx(ctx, vanchor, stream).await
181-
}
182-
},
183-
Command::Ping() => {
184-
let _ = stream.send(CommandResponse::Pong()).await;
185-
Ok(())
186-
}
187-
_ => {
188-
unimplemented!()
189-
}
190-
}
191-
}
192-
193-
/// Handler for fee estimation
194-
///
195-
/// # Arguments
196-
///
197-
/// * `chain_id` - ID of the blockchain
198-
/// * `vanchor` - Address of the smart contract
199-
/// * `gas_amount` - How much gas the transaction needs. Don't use U256 here because it
200-
/// gets parsed incorrectly.
201-
pub async fn handle_evm_fee_info(
202-
State(ctx): State<Arc<RelayerContext>>,
203-
Path((chain_id, vanchor, gas_amount)): Path<(u32, Address, u64)>,
204-
) -> Result<Json<EvmFeeInfo>, HandlerError> {
205-
let chain_id = TypedChainId::Evm(chain_id);
206-
let gas_amount = U256::from(gas_amount);
207-
Ok(
208-
get_evm_fee_info(chain_id, vanchor, gas_amount, ctx.as_ref())
209-
.await
210-
.map(Json)?,
211-
)
212-
}
213-
214-
/// Handler for fee estimation
215-
///
216-
/// # Arguments
217-
/// * `chain_id` - ID of the blockchain
218-
/// * `estimated_tx_fees` - Estimated transaction fees
219-
/// * `ctx` - RelayContext reference that holds the configuration
220-
pub async fn handle_substrate_fee_info(
221-
State(ctx): State<Arc<RelayerContext>>,
222-
Path((chain_id, estimated_tx_fees)): Path<(u64, u128)>,
223-
) -> Result<Json<SubstrateFeeInfo>, HandlerError> {
224-
get_substrate_fee_info(chain_id, estimated_tx_fees.into(), ctx.as_ref())
225-
.await
226-
.map(Json)
227-
.map_err(|e| {
228-
HandlerError(StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
229-
})
230-
}

0 commit comments

Comments
 (0)