Created
October 20, 2022 22:49
-
-
Save tilacog/3e370a7ecd3d2c103c018979ea8db41c to your computer and use it in GitHub Desktop.
Transaction manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::time::{Duration, Instant}; | |
/// How many confirmations to wait for | |
const REQUIRED_CONFIRMATIONS: u32 = 1; | |
const WAIT_TIME_IN_SECONDS: u64 = 60; | |
const HARD_TIMEOUT_FACTOR: u64 = 10; | |
/// Time before giving up on waiting for transaction confirmation. After this time, we expect the | |
/// Transaction Monitor to bump the gas price and re-broadcast the transaction. | |
const LIGHT_TIMEOUT: Duration = Duration::from_secs(WAIT_TIME_IN_SECONDS); | |
/// Hard time out to give up monitoring this transaction at all. After this time, we expect the | |
/// Transaction Monitor to return an error to the Oracle runner. | |
const HARD_TIMEOUT: Duration = Duration::from_secs(WAIT_TIME_IN_SECONDS * HARD_TIMEOUT_FACTOR); | |
/// Interval used to check for confirmation. Should be smaller than [`TIMEOUT`] | |
const SLEEP: Duration = Duration::from_secs(10); | |
/// | |
const GAS_BUMP_FACTOR: f32 = 1.2; | |
// stubs | |
struct ClientError; | |
enum MonitorError { | |
Client(ClientError), | |
TimedOut { confirmations: u32 }, | |
} | |
struct Client; | |
struct TransactionHash; | |
struct Transaction {} | |
/// Necessary data to create a signed transaction | |
struct TransactionInputs { | |
calldata: Vec<u8>, | |
signing_key: (), | |
gas_price: u64, | |
} | |
impl TransactionInputs { | |
fn sign(&self) -> Transaction { | |
Transaction {} | |
} | |
} | |
/// Monitors transaction confirmation. | |
struct TransactionMonitor { | |
/// Necessary to recreate the transaction in case gas price goes up | |
transaction_inputs: TransactionInputs, | |
/// JSON RPC client | |
client: Client, | |
} | |
impl TransactionMonitor { | |
fn broadcast(_transaction: Transaction) -> Result<TransactionHash, MonitorError> { | |
Ok(TransactionHash) // fake | |
} | |
/// Recalculates the gas price | |
fn bump_gas(&mut self, factor: f32) { | |
self.transaction_inputs.gas_price = | |
(self.transaction_inputs.gas_price as f32 * factor) as u64 | |
} | |
/// Fetches how many confirmations the transaction hash has | |
fn confirmations(&self, _transaction_hash: &TransactionHash) -> Result<u32, MonitorError> { | |
Ok(1) // fake | |
} | |
fn wait_for_confirmation(&mut self, hash: &TransactionHash) -> Result<(), MonitorError> { | |
let start = Instant::now(); | |
loop { | |
let confirmations = self.confirmations(&hash)?; | |
if confirmations > REQUIRED_CONFIRMATIONS { | |
return Ok(()); | |
} | |
// check if we timed out. | |
// we allow failing even if we have confirmations. | |
// it is up to the caller to decide if it should wait a little more or not | |
let elapsed = start.elapsed(); | |
if elapsed > LIGHT_TIMEOUT { | |
return Err(MonitorError::TimedOut { confirmations }); | |
} | |
// sleep and wait some more time | |
std::thread::sleep(SLEEP); | |
} | |
} | |
fn monitor(&mut self, hash: &TransactionHash) -> Result<(), MonitorError> { | |
let start = Instant::now(); | |
loop { | |
match self.wait_for_confirmation(hash) { | |
Ok(()) => break Ok(()), | |
Err(MonitorError::Client(client_error)) => { | |
// what should we do here? | |
// we might want to retry depending on the error's nature. | |
// remember that we use exponential backoff for JRPC calls. | |
todo!() | |
} | |
Err(MonitorError::TimedOut { confirmations }) => { | |
let elapsed = start.elapsed(); | |
let within_treshold = elapsed < HARD_TIMEOUT; | |
if confirmations == 0 && within_treshold { | |
// This is the typical case where we want to bump the gas price and | |
// re-broadcast the transaction. | |
self.bump_gas(GAS_BUMP_FACTOR); | |
let new_transaction = self.transaction_inputs.sign(); | |
Self::broadcast(new_transaction)?; | |
} else if within_treshold { | |
// what should we do if we time out with confirmations? | |
// maybe we can wait some more time before declare the failure of this | |
// transaction. | |
todo!() | |
} else { | |
// This is the worst case we can get. At this point, we tried to bump gas | |
// many times and still got no confirmations. | |
break Err(MonitorError::TimedOut { confirmations }); | |
} | |
} | |
} | |
} | |
} | |
} | |
fn main() { | |
println!("Hello, world!"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment