0xfirefist 1 tahun lalu
induk
melakukan
586b0cddce
2 mengubah file dengan 157 tambahan dan 5 penghapusan
  1. 61 2
      fortuna/src/chain/ethereum.rs
  2. 96 3
      fortuna/src/command/run.rs

+ 61 - 2
fortuna/src/chain/ethereum.rs

@@ -55,8 +55,7 @@ use {
 // contract in the same repo.
 abigen!(
     PythRandom,
-    // "../target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json"
-    "/Users/devkalra/Desktop/temp/EntropyUpgradable-std-output.json"
+    "../target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json"
 );
 
 pub type SignablePythContract = PythRandom<
@@ -64,6 +63,14 @@ pub type SignablePythContract = PythRandom<
 >;
 pub type PythContract = PythRandom<Provider<Http>>;
 
+pub struct RequestedWithCallbackFilter {
+    pub provider:           Address,
+    pub requestor:          Address,
+    pub sequence_number:    u64,
+    pub user_random_number: [u8; 32],
+    pub request:            Request,
+}
+
 /// Transformer that converts a transaction into a legacy transaction if use_legacy_tx is true.
 #[derive(Clone, Debug)]
 pub struct LegacyTxTransformer {
@@ -173,6 +180,58 @@ impl SignablePythContract {
             Err(anyhow!("Request failed").into())
         }
     }
+
+    pub async fn get_request_with_callback_events(
+        &self,
+        from_block: u64,
+        to_block: u64,
+    ) -> Result<Vec<RequestedWithCallbackFilter>> {
+        let mut event = self.requested_with_callback_filter();
+        event.filter = event.filter.from_block(from_block).to_block(to_block);
+
+        let res: Vec<RequestedWithCallbackFilter> = event.query().await?;
+
+        Ok(res)
+    }
+
+    async fn get_request(
+        &self,
+        provider_address: Address,
+        sequence_number: u64,
+    ) -> Result<Option<reader::Request>> {
+        let r = self
+            .get_request(provider_address, sequence_number)
+            // TODO: This doesn't work for lighlink right now. Figure out how to do this in lightlink
+            // .block(ethers::core::types::BlockNumber::Finalized)
+            .call()
+            .await?;
+
+        // sequence_number == 0 means the request does not exist.
+        if r.sequence_number != 0 {
+            Ok(Some(reader::Request {
+                provider:        r.provider,
+                sequence_number: r.sequence_number,
+                block_number:    r.block_number.try_into()?,
+                use_blockhash:   r.use_blockhash,
+            }))
+        } else {
+            Ok(None)
+        }
+    }
+
+    async fn get_block_number(&self, confirmed_block_status: BlockStatus) -> Result<BlockNumber> {
+        let block_number: EthersBlockNumber = confirmed_block_status.into();
+        let block = self
+            .client()
+            .get_block(block_number)
+            .await?
+            .ok_or_else(|| Error::msg("pending block confirmation"))?;
+
+        Ok(block
+            .number
+            .ok_or_else(|| Error::msg("pending confirmation"))?
+            .as_u64())
+    }
 }
 
 impl PythContract {

+ 96 - 3
fortuna/src/command/run.rs

@@ -1,10 +1,17 @@
 use {
     crate::{
         api,
-        chain::ethereum::PythContract,
+        chain::{
+            self,
+            ethereum::{
+                self,
+                PythContract,
+            },
+        },
         command::register_provider::CommitmentMetadata,
         config::{
             Config,
+            EthereumConfig,
             RunOptions,
         },
         state::{
@@ -16,17 +23,104 @@ use {
         anyhow,
         Result,
     },
-    axum::Router,
+    axum::{
+        response::sse::Event,
+        Router,
+    },
     std::{
         collections::HashMap,
+        os::unix::thread,
         sync::Arc,
+        thread::{
+            self,
+            sleep,
+        },
+        time::Duration,
     },
     tower_http::cors::CorsLayer,
     utoipa::OpenApi,
     utoipa_swagger_ui::SwaggerUi,
 };
 
+pub async fn handle_events(
+    events: Vec<ethereum::RequestedWithCallbackFilter>,
+    contract: &SignablePythContract,
+    chain_config: &EthereumConfig,
+) -> Result<()> {
+    for event in events {
+        let call = contract.reveal_with_callback(
+            event.request.provider,
+            event.request.sequence_number,
+            event.user_random_number,
+            // TODO: inject provider commitment here
+        );
+        let mut gas_estimate = call.estimate_gas().await?;
+        let gas_multiplier = U256::from(2); //TODO: smarter gas estimation
+        gas_estimate = gas_estimate * gas_multiplier;
+        let call_with_gas = call.gas(gas_estimate);
+        if let Some(r) = call_with_gas.send().await?.await? {
+            tracing::info!("Revealed: {:?}", r);
+        }
+    }
+
+    Ok(())
+}
+
+pub fn run_keeper(&chains: HashMap<api::ChainId, EthereumConfig>) -> Result<()> {
+    for (chain_id, chain_config) in chains {
+        thread::spawn(|| {
+            // Initialize a Provider to interface with the EVM contract.
+            let contract =
+                Arc::new(SignablePythContract::from_config(&chain_config, &private_key).await?);
+
+            let starting_block = contract
+                .get_block_number(chain_config.confirmed_block_status)
+                .await?;
+
+            // TODO: inject from the config
+            let block_backlog: u32 = 10_000;
+
+            // TODO: inject from the config
+            let sleep_duration: u32 = 5 * 60;
+
+            thread::spawn(|| {
+                let events = contract
+                    .get_request_with_callback_events(
+                        starting_block,
+                        // TODO: maybe add a check max of 0 or this number
+                        starting_block - block_backlog,
+                    )
+                    .await?;
+
+                handle_events(events, &contract, chain_config)
+            });
+
+            // every 5 minutes run get event
+            loop {
+                let events = contract
+                    .get_request_with_callback_events(
+                        starting_block,
+                        starting_block + block_backlog,
+                    )
+                    .await?;
+
+                handleEvents(events, &contract, &chain_config).await?;
+
+                sleep(Duration::from_secs(sleep_duration));
+            }
+        });
+    }
+    Ok(())
+}
+
 pub async fn run(opts: &RunOptions) -> Result<()> {
+    let config = Config::load(&opts.config.config)?;
+
+    // TODO: not sure if this is the right way
+    thread::spawn(|| {
+        run_keeper(&config.chains);
+    });
+
     #[derive(OpenApi)]
     #[openapi(
     paths(
@@ -46,7 +140,6 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
     )]
     struct ApiDoc;
 
-    let config = Config::load(&opts.config.config)?;
     let secret = opts.randomness.load_secret()?;