Browse Source

feat(Fortuna): add finality checks (#1322)

Dev Kalra 1 year ago
parent
commit
eaaa74a44e

+ 1 - 1
fortuna/Cargo.lock

@@ -1486,7 +1486,7 @@ dependencies = [
 
 [[package]]
 name = "fortuna"
-version = "3.2.4"
+version = "3.3.4"
 dependencies = [
  "anyhow",
  "axum",

+ 1 - 1
fortuna/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name    = "fortuna"
-version = "3.2.4"
+version = "3.3.4"
 edition = "2021"
 
 [dependencies]

+ 22 - 13
fortuna/src/api.rs

@@ -2,6 +2,7 @@ use {
     crate::{
         chain::reader::{
             BlockNumber,
+            BlockStatus,
             EntropyReader,
         },
         state::HashChainState,
@@ -73,14 +74,17 @@ impl ApiState {
 #[derive(Clone)]
 pub struct BlockchainState {
     /// The hash chain(s) required to serve random numbers for this blockchain
-    pub state:               Arc<HashChainState>,
+    pub state:                  Arc<HashChainState>,
     /// The contract that the server is fulfilling requests for.
-    pub contract:            Arc<dyn EntropyReader>,
+    pub contract:               Arc<dyn EntropyReader>,
     /// The address of the provider that this server is operating for.
-    pub provider_address:    Address,
+    pub provider_address:       Address,
     /// The server will wait for this many block confirmations of a request before revealing
     /// the random number.
-    pub reveal_delay_blocks: BlockNumber,
+    pub reveal_delay_blocks:    BlockNumber,
+    /// The BlockStatus of the block that is considered to be confirmed on the blockchain.
+    /// For eg., Finalized, Safe
+    pub confirmed_block_status: BlockStatus,
 }
 
 pub struct Metrics {
@@ -203,7 +207,10 @@ mod test {
                 BlockchainState,
                 GetRandomValueResponse,
             },
-            chain::reader::mock::MockEntropyReader,
+            chain::reader::{
+                mock::MockEntropyReader,
+                BlockStatus,
+            },
             state::{
                 HashChainState,
                 PebbleHashChain,
@@ -238,19 +245,21 @@ mod test {
         let eth_read = Arc::new(MockEntropyReader::with_requests(10, &[]));
 
         let eth_state = BlockchainState {
-            state:               ETH_CHAIN.clone(),
-            contract:            eth_read.clone(),
-            provider_address:    PROVIDER,
-            reveal_delay_blocks: 1,
+            state:                  ETH_CHAIN.clone(),
+            contract:               eth_read.clone(),
+            provider_address:       PROVIDER,
+            reveal_delay_blocks:    1,
+            confirmed_block_status: BlockStatus::Latest,
         };
 
         let avax_read = Arc::new(MockEntropyReader::with_requests(10, &[]));
 
         let avax_state = BlockchainState {
-            state:               AVAX_CHAIN.clone(),
-            contract:            avax_read.clone(),
-            provider_address:    PROVIDER,
-            reveal_delay_blocks: 2,
+            state:                  AVAX_CHAIN.clone(),
+            contract:               avax_read.clone(),
+            provider_address:       PROVIDER,
+            reveal_delay_blocks:    2,
+            confirmed_block_status: BlockStatus::Latest,
         };
 
         let api_state = ApiState::new(&[

+ 3 - 1
fortuna/src/api/revelation.rs

@@ -62,7 +62,9 @@ pub async fn revelation(
 
     let maybe_request_fut = state.contract.get_request(state.provider_address, sequence);
 
-    let current_block_number_fut = state.contract.get_block_number();
+    let current_block_number_fut = state
+        .contract
+        .get_block_number(state.confirmed_block_status);
 
     let (maybe_request, current_block_number) =
         try_join!(maybe_request_fut, current_block_number_fut).map_err(|e| {

+ 22 - 9
fortuna/src/chain/ethereum.rs

@@ -1,16 +1,16 @@
 use {
     crate::{
-        chain::{
-            reader,
-            reader::{
-                BlockNumber,
-                EntropyReader,
-            },
+        chain::reader::{
+            self,
+            BlockNumber,
+            BlockStatus,
+            EntropyReader,
         },
         config::EthereumConfig,
     },
     anyhow::{
         anyhow,
+        Error,
         Result,
     },
     axum::async_trait,
@@ -39,7 +39,10 @@ use {
             LocalWallet,
             Signer,
         },
-        types::transaction::eip2718::TypedTransaction,
+        types::{
+            transaction::eip2718::TypedTransaction,
+            BlockNumber as EthersBlockNumber,
+        },
     },
     sha3::{
         Digest,
@@ -209,7 +212,17 @@ impl EntropyReader for PythContract {
         }
     }
 
-    async fn get_block_number(&self) -> Result<BlockNumber> {
-        Ok(self.client().get_block_number().await?.as_u64())
+    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())
     }
 }

+ 33 - 3
fortuna/src/chain/reader.rs

@@ -1,11 +1,37 @@
 use {
     anyhow::Result,
     axum::async_trait,
-    ethers::types::Address,
+    ethers::types::{
+        Address,
+        BlockNumber as EthersBlockNumber,
+    },
 };
 
 pub type BlockNumber = u64;
 
+#[derive(
+    Copy, Clone, Debug, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize,
+)]
+pub enum BlockStatus {
+    /// Latest block
+    #[default]
+    Latest,
+    /// Finalized block accepted as canonical
+    Finalized,
+    /// Safe head block
+    Safe,
+}
+
+impl Into<EthersBlockNumber> for BlockStatus {
+    fn into(self) -> EthersBlockNumber {
+        match self {
+            BlockStatus::Latest => EthersBlockNumber::Latest,
+            BlockStatus::Finalized => EthersBlockNumber::Finalized,
+            BlockStatus::Safe => EthersBlockNumber::Safe,
+        }
+    }
+}
+
 /// EntropyReader is the read-only interface of the Entropy contract.
 #[async_trait]
 pub trait EntropyReader: Send + Sync {
@@ -15,7 +41,7 @@ pub trait EntropyReader: Send + Sync {
     async fn get_request(&self, provider: Address, sequence_number: u64)
         -> Result<Option<Request>>;
 
-    async fn get_block_number(&self) -> Result<BlockNumber>;
+    async fn get_block_number(&self, confirmed_block_status: BlockStatus) -> Result<BlockNumber>;
 }
 
 /// An in-flight request stored in the contract.
@@ -36,6 +62,7 @@ pub mod mock {
     use {
         crate::chain::reader::{
             BlockNumber,
+            BlockStatus,
             EntropyReader,
             Request,
         },
@@ -114,7 +141,10 @@ pub mod mock {
                 .map(|r| (*r).clone()))
         }
 
-        async fn get_block_number(&self) -> Result<BlockNumber> {
+        async fn get_block_number(
+            &self,
+            confirmed_block_status: BlockStatus,
+        ) -> Result<BlockNumber> {
             Ok(*self.block_number.read().unwrap())
         }
     }

+ 1 - 0
fortuna/src/command/run.rs

@@ -93,6 +93,7 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
             contract,
             provider_address: opts.provider,
             reveal_delay_blocks: chain_config.reveal_delay_blocks,
+            confirmed_block_status: chain_config.confirmed_block_status,
         };
 
         chains.insert(chain_id.clone(), state);

+ 13 - 2
fortuna/src/config.rs

@@ -1,7 +1,10 @@
 use {
     crate::{
         api::ChainId,
-        chain::reader::BlockNumber,
+        chain::reader::{
+            BlockNumber,
+            BlockStatus,
+        },
     },
     anyhow::{
         anyhow,
@@ -131,10 +134,18 @@ pub struct EthereumConfig {
     /// Address of a Pyth Randomness contract to interact with.
     pub contract_addr: Address,
 
-    /// How many blocks to wait before revealing the random number.
+    /// reveal_delay_blocks - The difference between the block number with the
+    /// confirmed_block_status(see below) and the block number of a request to
+    /// Entropy should be greater than `reveal_delay_blocks` for Fortuna to reveal
+    /// its commitment.
     pub reveal_delay_blocks: BlockNumber,
 
     /// Use the legacy transaction format (for networks without EIP 1559)
     #[serde(default)]
     pub legacy_tx: bool,
+
+    /// The BlockStatus of the block that is considered confirmed.
+    /// For example, Finalized, Safe, Latest
+    #[serde(default)]
+    pub confirmed_block_status: BlockStatus,
 }