# Relayer Service Entry => Main.rs ##### The main function is using the tokio crate to run asyncronous tasks, and it is using the clap crate to parse command line arguments ### main function execution => following Global varibables are being initilized 1. **settings** - from args (uses clap::parser) => config kaise 2. **async_redis** - to connect to a Redis server => learn more 3. **storage** - object to store last block number => worker 4. **eth_keypair** - from either the eth_secret command line argument or from the private_key field in the Settings struct. 5. **eth_contract_address** - from the Settings struct 6. **eth_contract_abi_settings** - by calling the get_contract_abi function from the eth_client crate. 7. **near_account** - from command line argument or from the file specified in the Settings struct 8. **near_contract_address** - from the Settings struct. 9. **stream** - of events from the Redis server 10. **subscriber** - processes incoming events 11. **pending_transactions_worker** - periodically checks for pending transactions on the Ethereum and NEAR networks. 12. **last_block_number_worker** - gets and stores new block number 13. **unlock_tokens_worker** - travesrse txn and unlock tokens on near 14. **rocket_conf** - get rocket config from settings 15. **rocket** - build a new instance of the Rocket web framework with the specified configuration main method in last uses **tokio::join!** to keep near_worker, subscriber, pending_transactions_worker, last_block_number_worker, unlock_tokens_worker, rocket.launch() to run these asynchronous tasks concurrently and wait for them to finish. ### Working of methods used in main #### 1. pub fn init(file_path: String) -> Result<Settings, String> path : spectre-bridge-service/src/config.rs Read the JSON contents from the file(path) passed as an argument, as an instance of `User` and return Settings as string which is then used to initillize one of the global variable `Settings`. #### 2. pub async fn connect(settings: std:\:sync::Arc<std:\:sync::Mutex\<crate::Settings>>) -> Self path : spectre-bridge-service/src/async_redis_wrapper.rs The method creates a Redis client using the `Client::open` method and the connection URL in the `redis_settings` struct. The `client` is then used to get a tokio-compatible connection to the Redis server using the `get_multiplexed_tokio_connection` method. If either of these operations fail, an error message is printed and the process is halted. If the connection is successful, a message is printed indicating that the connection has been established, and an AsyncRedisWrapper struct is returned. This struct contains the Redis client and connection objects. #### 3. pub fn new() -> Self path : spectre-bridge-service/src/last_block.rs The method creates and returns a new instance of the Storage struct, with the last_block_number field initialized to 0. #### 4. pub fn read_private_key_from_file(absolute_path: &str,) -> Result<near_crypto::InMemorySigner, String> path : spectre-bridge-service/near_client/src/read_private_key.rs This function reads a private key from a file at the specified absolute path and returns it as a `near_crypto::InMemorySigner` object. The file should contain a JSON object with two fields: `account_id` and private_key. The `account_id` field is a string representation of the account ID associated with the private key, and the `private_key` field is a string representation of the private key itself. The function first reads the contents of the file into a string, then uses the serde_json crate to parse the JSON string into a `serde_json::Value` object. It extracts the private_key and `account_id` fields from the `serde_json::Value` object and converts them into `near_crypto::SecretKey` and `near_primitives::types::AccountId` objects, respectively. Finally, it constructs a `near_crypto::InMemorySigner` object using the `near_crypto::InMemorySigner::from_secret_key` function and returns it. #### 5. pub fn subscribe<T: 'static + redis::FromRedisValue + Send>( channel: String, redis\:std:\:sync::Arc<std:\:sync::Mutex\<AsyncRedisWrapper>>,) -> redisResult<tokio:\:sync:\:mpsc::Receiver\<T>> path : This function creates a subscriber for a Redis channel and returns a `tokio::sync::mpsc::Receiver` object that can be used to receive messages published on the channel. The function takes two parameters: `channel`, a `String` representing the name of the channel to subscribe to, and `redis`, an `Arc` wrapping a Mutex containing an AsyncRedisWrapper object. It first creates a channel pair with `tokio::sync::mpsc::channel`, then spawns an async task to handle the subscription. Inside the task, a connection to Redis is obtained and converted into a pubsub connection with `into_pubsub`. The task then subscribes to the channel using the `subscribe` method and creates a stream of message events using `on_message`. The task enters a loop that waits for messages on the stream, fetches the message payload with `get_payload`, and sends it to the sender end of the channel pair. If an error occurs while sending the message, the loop is broken. Finally, the function returns the receiver end of the channel pair wrapped in an Ok variant of the `RedisResult` type. If any errors occur during the process, they will be returned as an Err variant of `RedisResult`. #### 6. pub async fn build_near_events_subscriber(settings\:std:\:sync::Arc<std:\:sync::Mutex\<Settings>>, eth_keypair: std:\:sync::Arc\<secp256k1::SecretKey>, redis: std:\:sync::Arc\<std:\:sync::Mutexcrate:\:async_redis_wrapper::AsyncRedisWrapper>>, eth_contract_abi: std:\:sync::Arc\<String>, eth_contract_address: std:\:sync::Arc\<web3:\:types::Address>,mut stream: tokio:\:sync:\:mpsc::Receiver\<String>,) -> impl futures_util::Future<Output = ()> This function is an async function that builds a subscriber for NEAR events. It takes six parameters: * `settings`, an `Arc` wrapping a `Mutex` containing a Settings object. * `eth_keypair`, an Arc wrapping a `secp256k1::SecretKey` object representing an Ethereum keypair. * `redis`, an `Arc` wrapping a `Mutex` containing an `AsyncRedisWrapper` object. * `eth_contract_abi`, an `Arc` wrapping a String containing the ABI of an Ethereum contract. * `eth_contract_address`, an `Arc` wrapping a `web3::types::Address` representing the address of an Ethereum contract. * `stream`, a `tokio::sync::mpsc::Receiver` object that receives messages published on a channel. The function returns a future that will run indefinitely and process NEAR events received on the channel. The function enters an infinite loop that waits for messages on the channel. When a message is received, it is deserialized into a `spectre_bridge_common::Event` object using `serde_json::from_str`. If the deserialization is successful, the event is matched against various event variants. If the event is a `SpectreBridgeInitTransferEvent`, it is passed to the `process_transfer_event` function for further processing. If the event is not a `SpectreBridgeInitTransferEvent`, it is ignored. If the deserialization fails, the message is discarded and the loop continues. #### 7. pub async fn build_pending_transactions_worker(settings: std:\:sync::Arc<std:\:sync::Mutex\<Settings>>, eth_keypair: std:\:sync::Arc\<secp256k1::SecretKey>, redis: crate:\:async_redis_wrapper::AsyncRedisWrapper, eth_contract_abi: std:\:sync:\:Arc\<String>, eth_contract_address: std:\:sync::Arc\<web3:\:types::Address>,) -> JoinHandle<()> This function is an async function that builds a worker for processing pending transactions. It takes five parameters: * `settings`, an Arc wrapping a Mutex containing a Settings object. * `eth_keypair`, an Arc wrapping a secp256k1::SecretKey object representing an Ethereum keypair. * `redis`, an AsyncRedisWrapper object. * `eth_contract_abi`, an `Arc` wrapping a String containing the ABI of an Ethereum contract. * `eth_contract_address`, an Arc wrapping a `web3::types::Address` representing the address of an Ethereum contract. The function returns a JoinHandle to a spawned task that will run the run function from the `pending_transactions_worker module`. The function first acquires a lock on the settings object to extract the `rpc_url`, `pending_transaction_poll_delay_sec`, and `rainbow_bridge_index_js_path` fields. It then creates an async block that calls the run function with these fields, as well as the `eth_contract_address`, `eth_contract_abi`, `eth_keypair`, and `redis` objects. The task is then spawned using `tokio::spawn` and the `JoinHandle` to the task is returned. #### 8. pub async fn last_block_number_worker(settings: std:\:sync::Arc\<std:\:sync::Mutex\<crate::Settings>>,storage: std:\:sync::Arc<std:\:sync::Mutex\<Storage>>,) This function appears to be starting a new task using the `tokio` library that will run indefinitely, performing the following actions in a loop: * Retrieve a copy of the `last_block_number_worker` settings from `Settings` struct that is stored in an `Arc\<Mutex>` and clone it. * Calculate an appropriate interval for making requests based on the `request_interval_secs` field of the `last_block_number_worker` settings. * Wait for the interval to elapse. * Call the `last_block_number` function with the `server_addr` and `contract_account_id` fields of the `last_block_number_worker` settings as arguments. * If the `last_block_number` function returns successfully, update the `last_block_number` field of `Storage` struct that is stored in an `Arc<Mutex>` with the result. If the function returns an error, print the error message to standard error. #### 9. pub async fn unlock_tokens_worker(account: near_crypto::InMemorySigner, gas: u64, settings: std:\:sync::Arc<std:\:sync::Mutex\<crate::Settings>>, storage: std:\:sync::Arc<std:\:sync::Mutex\<crate:\:last_block::Storage>>, redis: std:\:sync::Arc<std:\:sync::Mutex\<crate:\:async_redis_wrapper::AsyncRedisWrapper>>,) This function appears to be starting a new task using the tokio library that will run indefinitely, performing the following actions in a loop: * Retrieve a copy of the `unlock_tokens_worker` settings from `Settings` struct that is stored in an `Arc<Mutex>` and clone it. * Calculate an appropriate interval for making requests based on the `request_interval_secs` field of the `unlock_tokens_worker` settings. * Wait for the interval to elapse. * Retrieve the `tx_hashes` queue from Redis using the `get_tx_hashes` function. * If the `get_tx_hashes` function returns successfully, call the `transactions_traversal` function with the `account`, `gas`, `unlock_tokens_worker_settings`, `tx_hashes_queue`, `storage`, and `redis` arguments. If the function returns an error, print the error message to standard error. #### 10. pub fn build_rocket(conf: rocket::Config,settings: std:\:sync::Arc<std:\:sync::Mutex\<Settings>>, storage: std:\:sync::Arc<std:\:sync::Mutex\<crate:\:last_block::Storage>>, async_redis: std:\:sync::Arc<std:\:sync::Mutex\<crate:\:async_redis_wrapper::AsyncRedisWrapper>>,) This function appears to be building a new Rocket web server instance by: * Accepting a `rocket::Config` instance, `Settings` struct stored in an `Arc<Mutex>`, a `last_block::Storage` struct stored in an `Arc<Mutex>`, and a `async_redis_wrapper::AsyncRedisWrapper` struct stored in an `Arc<Mutex>` as arguments. * Building a new Rocket instance using `rocket::build()`. * Configuring the instance with the provided conf using the configure method. * Mounting the following routes to the `/v1` path: `health`, `transactions`, `set_threshold`, `set_allowed_tokens`, `profit`, `set_mapped_tokens`, `get_mapped_tokens`, `insert_mapped_tokens`, and `remove_mapped_tokens`. * Storing the `settings`, `storage`, and `async_redis` instances in the managed state of the Rocket instance using the manage method. * Returning the built and configured Rocket instance. ### Important methods used internally by above methods #### 1. pub fn process_transfer_event(nonce: near_sdk:\:json_types::U128, chain_id: u32, valid_till: u64, transfer: spectre_bridge_common::TransferDataEthereum, fee: spectre_bridge_common::TransferDataNear,recipient: spectre_bridge_common::EthAddress, settings: std:\:sync::Arc<std:\:sync::Mutex\<crate::Settings>>, redis: Arc<Mutex\<AsyncRedisWrapper>>,eth_contract_address: web3:\:types::Address, eth_key: std:\:sync::Arc\<secp256k1::SecretKey>, eth_contract_abi: std:\:sync::Arc\<String>,) This function appears to be starting a new task using the tokio library that will perform the following actions: * Clone the `settings` and `eth_contract_abi` arguments and store them in new variables. * Clone the `redis` argument and store it in a new variable. * Call the `execute_transfer` function with the `eth_key`, a `spectre_bridge_common::Event::SpectreBridgeInitTransferEvent` variant, `eth_contract_abi`, `rpc_url`, `eth_contract_address`, `0.0`, and `settings` as arguments, and store the result in a `tx_hash` variable. * If the `execute_transfer` function returns `Ok(Some(hash))`, store `PendingTransactionData` struct in the `PENDING_TRANSACTIONS` key of Redis. The `PendingTransactionData` struct contains the current timestamp and the `nonce` passed as an argument to this function. If the function returns an error, print the error message to standard error. If the function returns `Ok(None)`, print a message indicating that the transaction is not profitable. #### 2. pub async fn execute_transfer(key: impl web3:\:signing::Key, transfer_message: spectre_bridge_common::Event, contract_abi: &\[u8], rpc_url: &str, contract_addr: web3:\:types::Address, profit_threshold: f64, settings: Arc<Mutex\<crate::Settings>>,) -> Result<Option\<web3:\:types::H256>, crate:\:errors::CustomError> This function appears to be a function for executing a token transfer on Ethereum. It does the following: * Accepts the key to sign the transaction with, the details of the transfer to be made, the contract ABI, the RPC URL to connect to the Ethereum network, the address of the contract to call, and some other parameters. * Extracts the details of the transfer from the `transfer_message` parameter. * Calls the `estimate_gas` function to get the estimated gas needed to execute the transfer. If this fails, it returns an error. * Calls the `gas_price` function to get the current gas price. If this fails, it returns an error. * Calls the `eth_price` function to get the current price of Ethereum in USD. If this fails, it returns an error. * Calculates the estimated cost of executing the transfer by multiplying the estimated gas needed by the gas price. * Extracts the details of the fee to be paid for the transfer. * Calls the `get_coin_id` function to get the coin ID of the token that the fee will be paid in. If this fails, it returns an error. * Calls the token_price function to get the current price of the token in USD. If this fails, it returns an error. * Calculates the profit made from executing the transfer by subtracting the estimated cost of the transfer from the fee paid. * If the profit is greater than or equal to the `profit_threshold` parameter, it calls the `transferTokens` method on the contract with the extracted details of the transfer and the `nonce` parameter. It returns the transaction hash if the call is successful, or an error if it fails. * If the profit is less than the `profit_threshold` parameter, it returns `None`. #### 3. pub async fn run<'a>(rpc_url: url::Url,eth_contract_address: web3:\:types::Address, eth_contract_abi: String, eth_keypair: web3:\:signing::SecretKeyRef<'a>, rainbow_bridge_index_js_path: String, mut redis: crate:\:async_redis_wrapper::AsyncRedisWrapper, _delay_request_status_sec: u64,) This function appears to be a loop that runs indefinitely and performs the following tasks: * Connects to an Ethereum network using the `rpc_url` parameter and creates an `eth_client` object using the `RainbowBridgeEthereumClient` struct. * Initializes an empty `pending_transactions` hash map. * In a loop: * Queries a Redis database for all pending transactions and adds them to the `pending_transactions` hash map. * Iterates over the `pending_transactions` hash map, checking the status of each transaction using the `transaction_status` method of the `eth_client` object. If the transaction has failed, it logs an error and removes the transaction from the hash map. If the transaction has succeeded, it retrieves a proof of the transaction using the `get_proof` method of the `eth_client` object and stores the proof, along with the transaction's nonce and block number, in the Redis database. It then removes the transaction from the hash map. If the transaction is still pending, it updates the transaction's timestamp in the hash map. * Removes the processed transactions from the Redis database. * Waits for a specified number of seconds before starting the next iteration of the loop. #### Open questions 1. How can user calculate fee which is passed as part of transfer_message? 2. What happens when lp_relayer runs out of collateral ? 3. What happens when swap transaction in lp_relayer stage fails? 4. Where do relayers gets whitelisted and who controls relayer time slots and priorities? 5. Is the swap droppwd by relayer when it fails to get the token price, gas price or any other required values for any calculation #### Vulnarablities 1. Limitation of `eth_getTransactionReceipt` method for pending transactions > The `eth_getTransactionReceipt` method in the Ethereum JSON-RPC API can be used to retrieve the receipt of a completed transaction by providing the transaction hash as the input. However, this method will not work for transactions that are still pending, as the receipt for these transactions has not yet been generated. A pending transaction is a transaction that has been broadcast to the network, but has not yet been included in a block. Because a receipt is only generated when a transaction is included in a block, `eth_getTransactionReceipt` will return null when called on a transaction hash associated with a pending transaction. If you want to check the status of a pending transaction, you can use the eth_getTransactionByHash method, which will return information about the transaction including its current status (e.g. whether it has been mined or is still pending) 2. Rocket endpoints can cause denial of service if exposed publically >If an endpoint in a Rocket-based application is not properly secured and is exposed to the public internet, it can be targeted by malicious actors with the intent of causing a denial of service (`DoS`) attack. A `DoS` attack is an attempt to make a service, such as a website or network resource, unavailable to users by overwhelming it with excessive traffic or other forms of disruption. This can cause the targeted service to become unresponsive or to crash, leading to a temporary or permanent loss of availability. To prevent these kind of attack its very important to secure these endpoints and restrict access to only authorized users. 3. How are we ensuring that double spending does not happen >A `double-spend` attack in a trustless bridge occurs when an attacker is able to simultaneously spend the same cryptocurrency or token on two different blockchain networks connected by the bridge. This can happen if the bridge does not have adequate security measures in place to prevent the same assets from being transferred multiple times in quick succession. The attacker sends the same assets to the bridge contract on one blockchain, then quickly sends the same assets to a different address on the other blockchain before the bridge contract can confirm the transfer. The second transfer is typically faster, and the attacker is able to spend the same assets twice. This can result in the loss of funds for the recipients of the second transaction. It is important to mention that trustless bridges are still experimental technology, and some of them are not yet live on mainnet, the actual safety and robustness of the bridge's protocol to prevent those attacks can vary. 4. Race condition when multiple relayers are run which could cause loss of gas tokens >When multiple relayers will be added, race condition will occur i.e when more than one relayer will try to pick the same transaction, causing waste of gas tokens. This can be prevented by implementing proper synchronization mechanisms, such as locks or semaphores, to ensure that only one relayer can access the shared resource at a time. 5. Dependency on coinGecko API >Using an API like CoinGecko in a relayer can introduce several potential vulnerabilities, depending on how it is implemented. Here are a few ways in which dependency on the CoinGecko API can be a potential vulnerability for your relayer: >Single point of failure: If the CoinGecko API goes down or experiences any other kind of outage, it can cause features in your relayer to become unavailable or to provide inaccurate data to users. This can lead to user frustration and mistrust in your relayer. > >`Data integrity`: Depending on how you use the CoinGecko API, there may be a risk of data integrity issues. For example, if your relayer relies on the API to provide real-time cryptocurrency prices and that data is inaccurate or tampered with, it can lead to unexpected outcomes such as incorrect trades. > >`Security`: If the API is not properly secured, it can expose your relayer to security vulnerabilities. For example, if the API is prone to SQL injection attacks, it can be used to extract sensitive information from your relayer. > >`Financial loss`: If the API stops providing their service unexpectedly or facing legal issues or technical limitation that affects the service, your relayer can suffer from financial loss if the API is integral to the business model. > >It's important to use the API responsibly and to have a fallback plan in case the API becomes unavailable or unreliable. Additionally, it's important to keep your relayer's architecture as decoupled as possible, so that if one component, such as the CoinGecko API, goes down, it doesn't bring the entire relayer down with it. 6. Include finality check event for unlock tokens >It helps ensure that the tokens being transferred from one blockchain to another have been fully confirmed and processed by the original blockchain's network. This helps prevent the possibility of a token being transferred to the other blockchain before it has been fully confirmed and processed on the original blockchain, which could lead to the token being lost or stolen.